aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Somerville <matthew@mysociety.org>2011-06-10 14:56:00 +0100
committerMatthew Somerville <matthew@mysociety.org>2011-06-10 14:56:00 +0100
commit391ca1c469d93bb2c4798cc15e56fc495b5e80dd (patch)
tree6bc90fae589de824095e668fbf510ef259935729
parent7c96f8ec61d6eddc211f3f0e71cdb276c6a5f773 (diff)
parent860383f0de3287b0666d64a3ffff3db3a0f087ae (diff)
Merge branch 'migrate_to_catalyst' into reportemptyhomes
-rw-r--r--.gitignore11
-rw-r--r--Makefile.PL56
-rw-r--r--TODO.txt31
-rwxr-xr-xbin/cron-wrapper9
-rwxr-xr-xbin/gettext-extract47
-rwxr-xr-xbin/gettext-nget-patch42
-rwxr-xr-xbin/handlemail2
-rwxr-xr-xbin/import-flickr112
-rwxr-xr-xbin/send-alerts25
-rwxr-xr-xbin/send-questionnaires106
-rwxr-xr-xbin/send-questionnaires-eha114
-rwxr-xr-xbin/send-reports173
-rw-r--r--conf/.gitignore2
-rw-r--r--conf/cobrands/.gitignore1
-rw-r--r--conf/crontab.ugly26
-rw-r--r--conf/general-example6
-rw-r--r--conf/httpd.conf105
-rw-r--r--conf/httpd.conf-example68
-rw-r--r--conf/packages4
-rw-r--r--db/alert_types.pl8
-rw-r--r--db/alert_types.sql2
-rwxr-xr-xdb/rerun_dbic_loader.pl38
-rw-r--r--db/schema.sql38
-rw-r--r--db/schema_0001-add_sessions_and_users_and_dbic_fields.sql54
-rw-r--r--db/schema_0002-create_users_from_problems_and_link.sql53
-rw-r--r--db/schema_0003-create_users_from_alerts_and_link.sql29
-rw-r--r--db/schema_0004-create_users_from_comments_and_link.sql49
-rw-r--r--locale/FixMyStreet.po3053
-rw-r--r--locale/cy_GB.UTF-8/LC_MESSAGES/EmptyHomes.po5191
-rw-r--r--locale/nb_NO.UTF-8/LC_MESSAGES/FixMyStreet.po5107
-rw-r--r--notes/INSTALL2
-rw-r--r--notes/INSTALL-catalyst.txt108
-rw-r--r--notes/catalyst-master-merge-todos.txt9
-rw-r--r--notes/location_related_flow.txt23
-rw-r--r--perl-external/.gitignore1
-rwxr-xr-xperl-external/bin/cpanm5748
-rwxr-xr-xperl-external/bin/module-manage.pl265
-rw-r--r--perl-external/files.txt252
-rw-r--r--perl-external/lib/perl5/local/lib.pm1030
-rw-r--r--perl-external/minicpan/.gitignore3
-rw-r--r--perl-external/minicpan/modules/02packages.details.txt.gzbin0 -> 20125 bytes
-rw-r--r--perl-external/minicpan/modules/03modlist.data.gzbin0 -> 222 bytes
-rw-r--r--perl-external/modules.txt56
-rw-r--r--perllib/Carp/Always.pm162
-rw-r--r--perllib/Catalyst/Plugin/Session/State/Cookie.pm357
-rw-r--r--perllib/Cobrand.pm249
-rw-r--r--perllib/Cobrands/.gitignore1
-rw-r--r--perllib/Cobrands/Barnet/Util.pm117
-rw-r--r--perllib/Cobrands/Emptyhomes/Util.pm75
-rw-r--r--perllib/Cobrands/Fiksgatami/Util.pm70
-rw-r--r--perllib/Cobrands/Southampton/Util.pm117
-rw-r--r--perllib/CrossSell.pm16
-rw-r--r--perllib/FixMyStreet.pm158
-rw-r--r--perllib/FixMyStreet/Alert.pm347
-rw-r--r--perllib/FixMyStreet/App.pm394
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin.pm800
-rw-r--r--perllib/FixMyStreet/App/Controller/Alert.pm506
-rw-r--r--perllib/FixMyStreet/App/Controller/Around.pm300
-rw-r--r--perllib/FixMyStreet/App/Controller/Auth.pm240
-rw-r--r--perllib/FixMyStreet/App/Controller/Contact.pm237
-rw-r--r--perllib/FixMyStreet/App/Controller/Council.pm107
-rw-r--r--perllib/FixMyStreet/App/Controller/JSON.pm136
-rw-r--r--perllib/FixMyStreet/App/Controller/Location.pm113
-rw-r--r--perllib/FixMyStreet/App/Controller/My.pm36
-rw-r--r--perllib/FixMyStreet/App/Controller/Photo.pm93
-rwxr-xr-xperllib/FixMyStreet/App/Controller/Questionnaire.pm328
-rw-r--r--perllib/FixMyStreet/App/Controller/Report.pm160
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/New.pm1020
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/Update.pm321
-rw-r--r--perllib/FixMyStreet/App/Controller/Reports.pm359
-rw-r--r--perllib/FixMyStreet/App/Controller/Root.pm108
-rwxr-xr-xperllib/FixMyStreet/App/Controller/Rss.pm340
-rwxr-xr-xperllib/FixMyStreet/App/Controller/Static.pm56
-rw-r--r--perllib/FixMyStreet/App/Controller/Tilma.pm46
-rw-r--r--perllib/FixMyStreet/App/Controller/Tokens.pm218
-rw-r--r--perllib/FixMyStreet/App/Model/DB.pm24
-rw-r--r--perllib/FixMyStreet/App/Model/EmailSend.pm51
-rw-r--r--perllib/FixMyStreet/App/View/Email.pm44
-rw-r--r--perllib/FixMyStreet/App/View/Web.pm124
-rw-r--r--perllib/FixMyStreet/Cobrand.pm91
-rw-r--r--perllib/FixMyStreet/Cobrand/Barnet.pm82
-rw-r--r--perllib/FixMyStreet/Cobrand/Default.pm850
-rw-r--r--perllib/FixMyStreet/Cobrand/EmptyHomes.pm177
-rw-r--r--perllib/FixMyStreet/Cobrand/FiksGataMi.pm211
-rw-r--r--perllib/FixMyStreet/Cobrand/Southampton.pm82
-rw-r--r--perllib/FixMyStreet/DB.pm17
-rw-r--r--perllib/FixMyStreet/DB/Result/Abuse.pm21
-rw-r--r--perllib/FixMyStreet/DB/Result/AdminLog.pm44
-rw-r--r--perllib/FixMyStreet/DB/Result/Alert.pm127
-rw-r--r--perllib/FixMyStreet/DB/Result/AlertSent.pm38
-rw-r--r--perllib/FixMyStreet/DB/Result/AlertType.pm55
-rw-r--r--perllib/FixMyStreet/DB/Result/Comment.pm150
-rw-r--r--perllib/FixMyStreet/DB/Result/Contact.pm45
-rw-r--r--perllib/FixMyStreet/DB/Result/ContactsHistory.pm48
-rw-r--r--perllib/FixMyStreet/DB/Result/Nearby.pm33
-rw-r--r--perllib/FixMyStreet/DB/Result/Problem.pm386
-rw-r--r--perllib/FixMyStreet/DB/Result/Questionnaire.pm66
-rw-r--r--perllib/FixMyStreet/DB/Result/Secret.pm21
-rw-r--r--perllib/FixMyStreet/DB/Result/Session.pm28
-rw-r--r--perllib/FixMyStreet/DB/Result/Token.pm87
-rw-r--r--perllib/FixMyStreet/DB/Result/User.pm118
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/Alert.pm50
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/AlertType.pm204
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/Comment.pm41
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/Contact.pm33
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/Nearby.pm50
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/Problem.pm175
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/Questionnaire.pm146
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/User.pm8
-rw-r--r--perllib/FixMyStreet/Geocode.pm82
-rw-r--r--perllib/FixMyStreet/Map.pm46
-rw-r--r--perllib/FixMyStreet/Map/Bing.pm55
-rw-r--r--perllib/FixMyStreet/Map/BingOL.pm70
-rw-r--r--perllib/FixMyStreet/Map/FMS.pm62
-rw-r--r--perllib/FixMyStreet/Map/Google.pm52
-rw-r--r--perllib/FixMyStreet/Map/OSM.pm191
-rw-r--r--perllib/FixMyStreet/Map/OSM/CycleMap.pm4
-rw-r--r--perllib/FixMyStreet/Map/OSM/StreetView.pm56
-rw-r--r--perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm71
-rw-r--r--perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm69
-rw-r--r--perllib/FixMyStreet/Map/Tilma/OpenLayers.pm35
-rw-r--r--perllib/FixMyStreet/Map/Tilma/Original.pm190
-rw-r--r--perllib/FixMyStreet/Roles/Abuser.pm29
-rw-r--r--perllib/FixMyStreet/TestMech.pm494
-rw-r--r--perllib/Page.pm781
-rw-r--r--perllib/Problems.pm589
-rw-r--r--perllib/Standard.pm42
-rw-r--r--perllib/Utils.pm120
-rwxr-xr-xscript/fixmystreet_app_create.pl60
-rwxr-xr-xscript/fixmystreet_app_server.pl60
-rwxr-xr-xscript/fixmystreet_app_test.pl40
-rwxr-xr-xsetenv.pl85
-rwxr-xr-xt/Cobrand.t282
-rw-r--r--t/Cobrands/.gitignore1
-rw-r--r--t/Cobrands/Mysite/Util.pm109
-rwxr-xr-xt/Page.t107
-rwxr-xr-xt/Problems.t22
-rw-r--r--t/app/01app.t10
-rw-r--r--t/app/02pod.t10
-rw-r--r--t/app/03podcoverage.t14
-rw-r--r--t/app/controller/about.t31
-rw-r--r--t/app/controller/admin.t652
-rw-r--r--t/app/controller/alert.t66
-rw-r--r--t/app/controller/alert_new.t486
-rw-r--r--t/app/controller/around.t106
-rw-r--r--t/app/controller/auth.t227
-rw-r--r--t/app/controller/contact.t275
-rw-r--r--t/app/controller/council.t14
-rw-r--r--t/app/controller/index.t58
-rw-r--r--t/app/controller/json.t109
-rw-r--r--t/app/controller/my.t19
-rw-r--r--t/app/controller/page_not_found.t20
-rw-r--r--t/app/controller/questionnaire.t292
-rw-r--r--t/app/controller/report_display.t265
-rw-r--r--t/app/controller/report_import.t242
-rw-r--r--t/app/controller/report_new.t501
-rw-r--r--t/app/controller/report_updates.t885
-rw-r--r--t/app/controller/reports.t34
-rw-r--r--t/app/controller/sample.jpgbin0 -> 22588 bytes
-rw-r--r--t/app/controller/tilma.t12
-rw-r--r--t/app/helpers/send_email.t56
-rw-r--r--t/app/helpers/send_email_sample.txt29
-rw-r--r--t/app/load_general_config.t13
-rw-r--r--t/app/model/comment.t27
-rw-r--r--t/app/model/db.t8
-rw-r--r--t/app/model/problem.t155
-rw-r--r--t/app/model/token.t96
-rw-r--r--t/app/uri_for.t108
-rw-r--r--t/app/view/email.t8
-rw-r--r--t/app/view/web.t8
-rw-r--r--t/cobrand/loading.t71
-rw-r--r--t/fixmystreet.t37
-rw-r--r--t/i18n.t109
-rw-r--r--t/templates/mysite/test-header1
-rw-r--r--templates/email/default/alert-confirm.txt (renamed from templates/emails/alert-confirm)2
-rw-r--r--templates/email/default/alert-problem-area.txt (renamed from templates/emails/alert-problem-area)0
-rw-r--r--templates/email/default/alert-problem-council.txt (renamed from templates/emails/alert-problem-council)0
-rw-r--r--templates/email/default/alert-problem-nearby.txt (renamed from templates/emails/alert-problem-nearby)0
-rw-r--r--templates/email/default/alert-problem-ward.txt (renamed from templates/emails/alert-problem-ward)0
-rw-r--r--templates/email/default/alert-problem.txt (renamed from templates/emails/alert-problem)0
-rw-r--r--templates/email/default/alert-update.txt (renamed from templates/emails/alert-update)0
-rw-r--r--templates/email/default/contact.txt5
-rw-r--r--templates/email/default/login.txt12
-rw-r--r--templates/email/default/partial.txt (renamed from templates/emails/partial)6
-rw-r--r--templates/email/default/problem-confirm.txt (renamed from templates/emails/problem-confirm)8
-rw-r--r--templates/email/default/questionnaire.txt (renamed from templates/emails/questionnaire)0
-rw-r--r--templates/email/default/reply-autoresponse (renamed from templates/emails/reply-autoresponse)0
-rw-r--r--templates/email/default/submit-brent.txt (renamed from templates/emails/submit-brent)11
-rw-r--r--templates/email/default/submit.txt (renamed from templates/emails/submit-council)9
-rw-r--r--templates/email/default/test.txt15
-rw-r--r--templates/email/default/update-confirm.txt18
-rw-r--r--templates/email/emptyhomes/cy/alert-confirm.txt15
-rw-r--r--templates/email/emptyhomes/cy/alert-problem-area.txt13
-rw-r--r--templates/email/emptyhomes/cy/alert-problem-council.txt12
-rw-r--r--templates/email/emptyhomes/cy/alert-problem-nearby.txt11
-rw-r--r--templates/email/emptyhomes/cy/alert-problem-ward.txt13
-rw-r--r--templates/email/emptyhomes/cy/alert-problem.txt11
-rw-r--r--templates/email/emptyhomes/cy/alert-update.txt13
-rw-r--r--templates/email/emptyhomes/cy/partial.txt16
-rw-r--r--templates/email/emptyhomes/cy/problem-confirm.txt21
-rw-r--r--templates/email/emptyhomes/cy/questionnaire-26weeks.txt25
-rw-r--r--templates/email/emptyhomes/cy/questionnaire-4weeks.txt25
-rw-r--r--templates/email/emptyhomes/cy/submit.txt47
-rw-r--r--templates/email/emptyhomes/cy/update-confirm.txt18
-rw-r--r--templates/email/emptyhomes/en-gb/alert-confirm.txt14
-rw-r--r--templates/email/emptyhomes/en-gb/alert-problem-area.txt (renamed from templates/emails/emptyhomes/alert-problem-area)0
-rw-r--r--templates/email/emptyhomes/en-gb/alert-problem-council.txt (renamed from templates/emails/emptyhomes/alert-problem-council)0
-rw-r--r--templates/email/emptyhomes/en-gb/alert-problem-nearby.txt (renamed from templates/emails/emptyhomes/alert-problem-nearby)0
-rw-r--r--templates/email/emptyhomes/en-gb/alert-problem-ward.txt (renamed from templates/emails/emptyhomes/alert-problem-ward)0
-rw-r--r--templates/email/emptyhomes/en-gb/alert-problem.txt (renamed from templates/emails/emptyhomes/alert-problem)0
-rw-r--r--templates/email/emptyhomes/en-gb/alert-update.txt (renamed from templates/emails/emptyhomes/alert-update)0
-rw-r--r--templates/email/emptyhomes/en-gb/contact.txt5
-rw-r--r--templates/email/emptyhomes/en-gb/partial.txt14
-rw-r--r--templates/email/emptyhomes/en-gb/problem-confirm.txt (renamed from templates/emails/empty property-confirm)8
-rw-r--r--templates/email/emptyhomes/en-gb/questionnaire-26weeks.txt (renamed from templates/emails/questionnaire-eha-26weeks)0
-rw-r--r--templates/email/emptyhomes/en-gb/questionnaire-4weeks.txt (renamed from templates/emails/questionnaire-eha-4weeks)0
-rw-r--r--templates/email/emptyhomes/en-gb/submit.txt (renamed from templates/emails/submit-eha)0
-rw-r--r--templates/email/emptyhomes/en-gb/update-confirm.txt17
-rw-r--r--templates/email/fiksgatami/alert-confirm.txt14
-rw-r--r--templates/email/fiksgatami/alert-problem-area.txt13
-rw-r--r--templates/email/fiksgatami/alert-problem-council.txt12
-rw-r--r--templates/email/fiksgatami/alert-problem-nearby.txt11
-rw-r--r--templates/email/fiksgatami/alert-problem-ward.txt13
-rw-r--r--templates/email/fiksgatami/alert-problem.txt11
-rw-r--r--templates/email/fiksgatami/alert-update.txt18
-rw-r--r--templates/email/fiksgatami/contact.txt5
-rw-r--r--templates/email/fiksgatami/partial.txt14
-rw-r--r--templates/email/fiksgatami/problem-confirm.txt20
-rw-r--r--templates/email/fiksgatami/questionnaire.txt23
-rw-r--r--templates/email/fiksgatami/submit.txt41
-rw-r--r--templates/email/fiksgatami/update-confirm.txt18
-rw-r--r--templates/emails/.gitignore1
-rw-r--r--templates/emails/flickr-confirm14
-rw-r--r--templates/emails/flickr-submit14
-rw-r--r--templates/emails/tms-confirm14
-rw-r--r--templates/emails/update-confirm17
-rw-r--r--templates/web/barnet/footer.html (renamed from templates/website/cobrands/barnet/footer)302
-rw-r--r--templates/web/barnet/header.html (renamed from templates/website/cobrands/barnet/header)191
-rw-r--r--templates/web/default/admin/council_contacts.html89
-rw-r--r--templates/web/default/admin/council_contacts.txt4
-rw-r--r--templates/web/default/admin/council_edit.html62
-rw-r--r--templates/web/default/admin/council_list.html47
-rw-r--r--templates/web/default/admin/footer.html2
-rw-r--r--templates/web/default/admin/header.html17
-rw-r--r--templates/web/default/admin/index.html31
-rw-r--r--templates/web/default/admin/list_updates.html34
-rw-r--r--templates/web/default/admin/questionnaire.html21
-rw-r--r--templates/web/default/admin/report_blocks.html7
-rw-r--r--templates/web/default/admin/report_edit.html50
-rw-r--r--templates/web/default/admin/search_reports.html61
-rw-r--r--templates/web/default/admin/timeline.html46
-rw-r--r--templates/web/default/admin/update_edit.html38
-rw-r--r--templates/web/default/alert/choose.html14
-rw-r--r--templates/web/default/alert/index.html39
-rw-r--r--templates/web/default/alert/list.html129
-rw-r--r--templates/web/default/alert/updates.html22
-rw-r--r--templates/web/default/around/around_index.html50
-rw-r--r--templates/web/default/around/around_map_list_items.html16
-rwxr-xr-xtemplates/web/default/around/display_location.html128
-rw-r--r--templates/web/default/around/on_map_list_items.html13
-rw-r--r--templates/web/default/auth/change_password.html41
-rw-r--r--templates/web/default/auth/general.html53
-rw-r--r--templates/web/default/auth/logout.html8
-rw-r--r--templates/web/default/auth/token.html25
-rw-r--r--templates/web/default/common_header_tags.html18
-rw-r--r--templates/web/default/contact/address.html11
-rw-r--r--templates/web/default/contact/blurb.html9
-rw-r--r--templates/web/default/contact/index.html101
-rw-r--r--templates/web/default/contact/submit.html20
-rw-r--r--templates/web/default/debug_footer.html40
-rw-r--r--templates/web/default/debug_header.html5
-rw-r--r--templates/web/default/email_sent.html34
-rw-r--r--templates/web/default/errors.html5
-rw-r--r--templates/web/default/errors/page_error_404_not_found.html20
-rw-r--r--templates/web/default/errors/page_error_410_gone.html20
-rwxr-xr-xtemplates/web/default/faq/faq-en-gb.html (renamed from templates/website/faq-en-gb)10
-rw-r--r--templates/web/default/footer.html24
-rw-r--r--templates/web/default/front_stats.html46
-rw-r--r--templates/web/default/header.html21
-rw-r--r--templates/web/default/index.html107
-rw-r--r--templates/web/default/maps/bing.html18
-rw-r--r--templates/web/default/maps/fms.html11
-rw-r--r--templates/web/default/maps/google.html17
-rw-r--r--templates/web/default/maps/openlayers.html86
-rw-r--r--templates/web/default/maps/osm-streetview.html9
-rw-r--r--templates/web/default/maps/osm.html9
-rw-r--r--templates/web/default/maps/pins_js.html4
-rw-r--r--templates/web/default/maps/tilma/openlayers.html26
-rw-r--r--templates/web/default/maps/tilma/original.html88
-rw-r--r--templates/web/default/my/my.html18
-rw-r--r--templates/web/default/questionnaire/completed.html28
-rw-r--r--templates/web/default/questionnaire/creator_fixed.html23
-rwxr-xr-xtemplates/web/default/questionnaire/error.html8
-rw-r--r--templates/web/default/questionnaire/index.html121
-rw-r--r--templates/web/default/report/_main.html18
-rw-r--r--templates/web/default/report/display.html150
-rw-r--r--templates/web/default/report/new/all_councils_text.html19
-rw-r--r--templates/web/default/report/new/fill_in_details.html180
-rw-r--r--templates/web/default/report/new/fill_in_details_text.html10
-rw-r--r--templates/web/default/report/new/form_heading.html0
-rw-r--r--templates/web/default/report/new/no_councils_text.html20
-rw-r--r--templates/web/default/report/new/notes.html11
-rw-r--r--templates/web/default/report/new/report_import.html92
-rw-r--r--templates/web/default/report/new/some_councils_text.html28
-rw-r--r--templates/web/default/report/photo.html6
-rw-r--r--templates/web/default/report/updates.html32
-rwxr-xr-xtemplates/web/default/reports/council.html99
-rwxr-xr-xtemplates/web/default/reports/index.html35
-rwxr-xr-xtemplates/web/default/reports/ward.html1
-rw-r--r--templates/web/default/static/about.html9
-rw-r--r--templates/web/default/static/fun.html35
-rwxr-xr-xtemplates/web/default/static/iphone.html52
-rw-r--r--templates/web/default/static/posters.html43
-rw-r--r--templates/web/default/tokens/abuse.html7
-rw-r--r--templates/web/default/tokens/confirm_alert.html17
-rw-r--r--templates/web/default/tokens/confirm_problem.html22
-rw-r--r--templates/web/default/tokens/confirm_update.html20
-rw-r--r--templates/web/default/tokens/error.html9
-rw-r--r--templates/web/default/tracking_code.html16
-rw-r--r--templates/web/emptyhomes/contact/address.html0
-rw-r--r--templates/web/emptyhomes/contact/blurb.html3
-rw-r--r--templates/web/emptyhomes/faq/faq-cy.html (renamed from templates/website/cobrands/emptyhomes/faq-cy)6
-rwxr-xr-xtemplates/web/emptyhomes/faq/faq-en-gb.html (renamed from templates/website/cobrands/emptyhomes/faq-en-gb)6
-rw-r--r--templates/web/emptyhomes/footer.html (renamed from templates/website/cobrands/emptyhomes/footer)4
-rw-r--r--templates/web/emptyhomes/header.html47
-rw-r--r--templates/web/emptyhomes/report/new/all_councils_text.html8
-rw-r--r--templates/web/emptyhomes/report/new/fill_in_details_text.html6
-rw-r--r--templates/web/emptyhomes/report/new/form_heading.html1
-rw-r--r--templates/web/emptyhomes/report/new/no_councils_text.html10
-rw-r--r--templates/web/emptyhomes/report/new/notes.html1
-rwxr-xr-xtemplates/web/emptyhomes/reports/index.html33
-rw-r--r--templates/web/emptyhomes/static/about.html43
-rw-r--r--templates/web/emptyhomes/tokens/confirm_problem.html42
-rw-r--r--templates/web/emptyhomes/tracking_code.html12
-rw-r--r--templates/web/fiksgatami/contact/address.html0
-rw-r--r--templates/web/fiksgatami/faq/faq-nb.html (renamed from templates/website/cobrands/fiksgatami/faq-nb)4
-rw-r--r--templates/web/fiksgatami/footer.html23
-rw-r--r--templates/web/fiksgatami/header.html19
-rwxr-xr-xtemplates/web/fiksgatami/reports/index.html34
-rw-r--r--templates/web/southampton/footer.html (renamed from templates/website/cobrands/southampton/footer)0
-rw-r--r--templates/web/southampton/header.html (renamed from templates/website/cobrands/southampton/header)8
-rw-r--r--templates/website/cobrands/.gitignore1
-rw-r--r--templates/website/cobrands/emptyhomes/header41
-rw-r--r--templates/website/cobrands/fiksgatami/footer16
-rw-r--r--templates/website/cobrands/fiksgatami/header19
-rw-r--r--templates/website/contact42
-rw-r--r--templates/website/header19
-rwxr-xr-xtemplates/website/map28
-rw-r--r--templates/website/problem62
-rw-r--r--templates/website/questionnaire34
-rw-r--r--templates/website/report-form68
-rw-r--r--templates/website/reports12
-rw-r--r--templates/website/scambs-footer19
-rw-r--r--templates/website/scambs-header82
-rw-r--r--urls.txt12
-rw-r--r--web-admin/.gitignore1
-rwxr-xr-xweb-admin/index.cgi971
-rwxr-xr-xweb/about.cgi52
-rwxr-xr-xweb/ajax.cgi107
-rwxr-xr-xweb/alert.cgi610
-rw-r--r--web/cobrands/.gitignore1
-rw-r--r--web/cobrands/emptyhomes/css.css (renamed from web/css/cobrands/emptyhomes/emptyhomes.css)0
-rwxr-xr-xweb/confirm.cgi247
-rwxr-xr-xweb/contact.cgi245
-rw-r--r--web/css/cobrands/.gitignore1
-rw-r--r--web/css/core.css2
-rw-r--r--web/css/core.scss2
-rw-r--r--web/css/scambs.css1110
-rwxr-xr-xweb/faq.cgi24
-rwxr-xr-xweb/fixmystreet_app_cgi.cgi35
-rwxr-xr-xweb/fixmystreet_app_fastcgi.cgi53
-rwxr-xr-xweb/flickr.cgi101
-rwxr-xr-xweb/fun.cgi56
-rwxr-xr-xweb/import.cgi173
-rwxr-xr-xweb/index.cgi1163
-rwxr-xr-xweb/iphone/index.cgi73
-rw-r--r--web/js.js169
-rw-r--r--web/js/fixmystreet.js78
-rw-r--r--web/js/map-OpenLayers.js6
-rw-r--r--web/js/map-bing-ol.js2
-rw-r--r--web/js/map-tilma.js4
-rwxr-xr-xweb/json.cgi35
-rwxr-xr-xweb/photo.cgi54
-rwxr-xr-xweb/posters/index.cgi64
-rwxr-xr-xweb/questionnaire.cgi338
-rwxr-xr-xweb/reports.cgi341
-rwxr-xr-xweb/rss.cgi169
-rwxr-xr-xweb/test.cgi25
-rwxr-xr-xweb/tms-signup.cgi120
-rwxr-xr-xweb/upload.cgi56
-rw-r--r--web/xsl.eha.xsl2
-rw-r--r--web/xsl.xsl2
392 files changed, 34389 insertions, 19184 deletions
diff --git a/.gitignore b/.gitignore
index 7d42f6e82..073bd7ab7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,13 @@
/cities_bin
/private_locale
._*
-.sass-cache
-perl-external/
+.DS_Store
+local-lib5
+META.yml
+Makefile
+blib/
inc/
+.sass-cache
+_Inline/
+lib
+tags \ No newline at end of file
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 000000000..aba781785
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,56 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use inc::Module::Install;
+use Module::Install::Catalyst;
+
+name 'FixMyStreet-App';
+all_from 'perllib/FixMyStreet/App.pm';
+
+requires 'Catalyst::Action::RenderView';
+requires 'Catalyst::Authentication::Store::DBIx::Class';
+requires 'Catalyst::Model::Adaptor';
+requires 'Catalyst::Plugin::Authentication';
+requires 'Catalyst::Plugin::ConfigLoader';
+requires 'Catalyst::Plugin::Session::Store::DBIC';
+requires 'Catalyst::Plugin::Static::Simple';
+requires 'Catalyst::Runtime' => '5.80031';
+requires 'Catalyst::View::TT';
+requires 'Config::General';
+requires 'DBD::Pg' => '2.9.2';
+requires 'DBIx::Class::Schema::Loader';
+requires 'Email::Send';
+requires 'Email::Simple';
+requires 'Email::Valid';
+requires 'HTML::Entities';
+requires 'IO::String';
+requires 'Moose';
+requires 'namespace::autoclean';
+requires 'Net::Domain::TLD';
+requires 'Path::Class';
+requires 'Readonly';
+requires 'Sort::Key';
+requires 'List::MoreUtils';
+requires 'IPC::Run3';
+requires 'Data::UUID';
+requires 'YAML';
+requires 'Geography::NationalGrid';
+requires 'Digest::SHA1';
+requires 'XML::RSS';
+requires 'Regexp::Common';
+requires 'Text::Template';
+requires 'Image::Size';
+requires 'Image::Magick';
+requires 'Statistics::Distributions';
+
+test_requires 'Test::More' => '0.88';
+test_requires 'Test::WWW::Mechanize::Catalyst';
+test_requires 'Sub::Override';
+
+catalyst;
+
+install_script glob('script/*.pl');
+auto_install;
+WriteAll;
diff --git a/TODO.txt b/TODO.txt
new file mode 100644
index 000000000..8d90a4b64
--- /dev/null
+++ b/TODO.txt
@@ -0,0 +1,31 @@
+
+Add users to system:
+ * script to migrate database
+ * best phone for users from problems
+
+Auth:
+ * add 'remember me' option on login.
+ * limit session to this browser session on create account
+ * redirect back to page they came from on login
+
+Problem creation:
+ ? what should the new flow be for not-logged-in (probably unchanged).
+ * after becoming confirmed require user to manually send off 'pending' reports
+
+Users:
+ * create a message to the user if they have reports which have not been
+ confirmed.
+
+Email:
+ * currently don't send email via EvEl or do any of the smarts it does - should
+ we switch to using it (Email::Send::EvEl...)?
+
+Framework:
+ * use Plack to handle all the redirects and also run cgi scripts - get apache
+ out of the picture (what about tile proxy...). Does this makes sense?
+
+
+
+Future ideas:
+ * dashboard for council to put on big screen
+ \ No newline at end of file
diff --git a/bin/cron-wrapper b/bin/cron-wrapper
new file mode 100755
index 000000000..b93695cb0
--- /dev/null
+++ b/bin/cron-wrapper
@@ -0,0 +1,9 @@
+#!/usr/bin/env perl
+
+BEGIN { # set all the paths to the perl code
+ use FindBin;
+ require "$FindBin::Bin/../setenv.pl";
+}
+
+exec { $ARGV[0] } @ARGV;
+
diff --git a/bin/gettext-extract b/bin/gettext-extract
index 24defd014..55623e86c 100755
--- a/bin/gettext-extract
+++ b/bin/gettext-extract
@@ -4,10 +4,8 @@
# Generate English language .po files from the source code and email templates,
# for FixMyStreet. Writes the output to appropriate .po files in locale/.
#
-# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved.
+# Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved.
# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/
-#
-# $Id: gettext-extract,v 1.14 2009-11-30 10:21:52 louise Exp $
if [ -e ../../locale ]
then
@@ -25,56 +23,31 @@ fi
fi
fi
-# Take chunk of text and escape each line in it for putting in catalogue
-function plain_gettext_escape() {
- IFS=""
- while read LINE
- do
- LINE=${LINE//\"/\\\"}
- echo \"$LINE\\n\"
- done
-}
-
# File to write to, clear it to start with
PO=locale/FixMyStreet.po
rm -f $PO
# Extract from Perl
-xgettext --add-comments=TRANS --language=Perl --keyword=_ --keyword=nget:1,2 --from-code=utf-8 -o $PO `find perllib -name "*.pm"` web/*.cgi bin/send-* db/*.pl web-admin/*.cgi
+xgettext.pl --gnu-gettext --verbose --output $PO --plugin perl=* --plugin tt2 --directory perllib --directory templates/web --directory db --directory bin
# Fix headers
TEMP=`tempfile`
+NOW=`date +"%Y-%m-%d %H:%M%z"`
cat $PO | sed "
s/SOME DESCRIPTIVE TITLE/FixMyStreet original .po file, autogenerated by gettext-extract/;
- s/YEAR THE PACKAGE'S COPYRIGHT HOLDER/2008 UK Citizens Online Democracy/;
+ s/YEAR THE PACKAGE'S COPYRIGHT HOLDER/2011 UK Citizens Online Democracy/;
s/PACKAGE package/main FixMyStreet code/;
- s/FIRST AUTHOR <EMAIL@ADDRESS>, YEAR./Matthew Somerville <matthew@mysociety.org>, 2008-04-15./;
+ s/FIRST AUTHOR <EMAIL@ADDRESS>, YEAR./Matthew Somerville <matthew@mysociety.org>, 2011-06-03./;
- s/PACKAGE VERSION/1.0/;
- s/Report-Msgid-Bugs-To: /Report-Msgid-Bugs-To: matthew@mysociety.org/;
+ s/PACKAGE VERSION/1.0\\\n\"\n\"Report-Msgid-Bugs-To: matthew@mysociety.org/;
+ s/POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE/POT-Creation-Date: $NOW/;
s/LL@li.org/team@fixmystreet.com/;
- s/charset=CHARSET/charset=UTF-8/;
+ s/charset=CHARSET/charset=UTF-8/;
+ s/8bit/8bit\\\n\"\n\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;/;
" >> $TEMP
mv $TEMP $PO
-# XXX The XSL page needs including?
-
-# Extract email templates
-echo >> $PO
-echo '#. Please leave the first word "Subject:" untranslated' >> $PO
-for X in templates/emails/* templates/emails/emptyhomes/*
-do
- # TODO: Should check for "*~" type filenames too, and do the *-livesimply case
- # with wildcards rather than checking per template
- if [ ! -d "$X" ]
- then
- echo >> $PO
- echo "#: $X" >> $PO
- echo msgid \"\" >> $PO
- cat "$X" | plain_gettext_escape >> $PO
- echo msgstr \"\" >> $PO
- fi
-done
+echo "$( bin/gettext-nget-patch )" >> $PO
bin/make_emptyhomes_po
diff --git a/bin/gettext-nget-patch b/bin/gettext-nget-patch
new file mode 100755
index 000000000..223bcc816
--- /dev/null
+++ b/bin/gettext-nget-patch
@@ -0,0 +1,42 @@
+#!/usr/bin/perl
+#
+# xgettext doesn't deal with TT files, but xgettext.pl doesn't find nget()s, sigh.
+# This will find the nget()s and output a .po file excerpt.
+
+use File::Find qw/find/;
+
+my %out;
+
+find( sub {
+ next unless -f;
+ open (FP, $_) or die $!;
+ while (<FP>) {
+ next unless /nget/;
+ my $line = $.;
+ my $text = $_;
+ do {
+ $text .= <FP>;
+ } until $text =~ /\)/;
+ $text =~ /nget\(\s*"(.*?)"\s*,\s*"(.*?)"\s*,\s*(.*?)\s*\)/s;
+ $out{$1} = {
+ file => $File::Find::name,
+ line => $line,
+ s => $1,
+ p => $2,
+ };
+ }
+ close FP;
+}, 'templates');
+
+foreach (values %out) {
+ print <<EOF;
+
+#: $_->{file}:$_->{line}
+#, perl-format
+msgid "$_->{s}"
+msgid_plural "$_->{p}"
+msgstr[0] ""
+msgstr[1] ""
+EOF
+}
+
diff --git a/bin/handlemail b/bin/handlemail
index c5854a9ab..8b8e03be9 100755
--- a/bin/handlemail
+++ b/bin/handlemail
@@ -47,7 +47,7 @@ if ($data{is_bounce_message}) {
# Not a bounce, send an automatic response
my $template = 'reply-autoresponse';
-open FP, "$FindBin::Bin/../templates/emails/$template" or exit 75;
+open FP, "$FindBin::Bin/../templates/email/default/$template" or exit 75;
$template = join('', <FP>);
close FP;
diff --git a/bin/import-flickr b/bin/import-flickr
deleted file mode 100755
index f4a838547..000000000
--- a/bin/import-flickr
+++ /dev/null
@@ -1,112 +0,0 @@
-#!/usr/bin/perl -w
-
-# import-flickr:
-# Get new Flickr photos (uploaded from cameras, hopefully!)
-#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: import-flickr,v 1.9 2008-10-09 17:18:02 matthew Exp $
-
-use strict;
-require 5.8.0;
-
-# Horrible boilerplate to set up appropriate library paths.
-use FindBin;
-use lib "$FindBin::Bin/../perllib";
-use lib "$FindBin::Bin/../commonlib/perllib";
-use File::Slurp;
-use LWP::Simple;
-
-use Utils;
-use mySociety::AuthToken;
-use mySociety::Config;
-use mySociety::DBHandle qw(dbh select_all);
-use mySociety::EmailUtil;
-use mySociety::Email;
-
-BEGIN {
- mySociety::Config::set_file("$FindBin::Bin/../conf/general");
- mySociety::DBHandle::configure(
- Name => mySociety::Config::get('BCI_DB_NAME'),
- User => mySociety::Config::get('BCI_DB_USER'),
- Password => mySociety::Config::get('BCI_DB_PASS'),
- Host => mySociety::Config::get('BCI_DB_HOST', undef),
- Port => mySociety::Config::get('BCI_DB_PORT', undef)
- );
-}
-
-my $key = mySociety::Config::get('FLICKR_API');
-my $url = 'http://api.flickr.com/services/rest/?method=flickr.photos.search&tags=fixmystreet&extras=geo,machine_tags&api_key=' . $key . '&user_id=';
-my $ids = select_all("select nsid from partial_user where service='flickr'");
-my $result = '';
-foreach (@$ids) {
- my $api_lookup = get($url . $_->{nsid});
- next unless $api_lookup;
- $result .= $api_lookup;
-}
-
-my %ids;
-my $st = select_all('select id from flickr_imported');
-foreach (@$st) {
- $ids{$_->{id}} = 1;
-}
-
-# XXX: Hmm... Use format=perl now Cal has added it for me! :)
-while ($result =~ /<photo id="([^"]*)" owner="([^"]*)" secret="([^"]*)" server="([^"]*)" farm="([^"]*)" title="([^"]*)".*?latitude="([^"]*)" longitude="([^"]*)".*?machine_tags="([^"]*)"/g) {
- my ($id, $owner, $secret, $server, $farm, $title, $latitude, $longitude, $machine) = ($1, $2, $3, $4, $5, $6, $7, $8, $9);
- next if $ids{$id};
- if ($machine =~ /geo:/ && !$latitude && !$longitude) {
- # Have to fetch raw tags, as otherwise can't tell if it's negative, or how many decimal places
- my $url = 'http://api.flickr.com/services/rest/?method=flickr.tags.getListPhoto&api_key=' . $key . '&photo_id=' . $id;
- my $tags = get($url);
- ($longitude) = $tags =~ /raw="geo:lon=([^"]*)"/i;
- ($latitude) = $tags =~ /raw="geo:lat=([^"]*)"/i;
- }
- my $url = "http://farm$farm.static.flickr.com/$server/".$id.'_'.$secret.'_m.jpg';
- my $image = get($url);
- problem_create($id, $owner, $title, $latitude, $longitude, $image);
-}
-
-sub problem_create {
- my ($photo_id, $owner, $title, $latitude, $longitude, $image) = @_;
- my ($name, $email) = dbh()->selectrow_array("select name, email from partial_user where service='flickr' and nsid=?", {}, $owner);
-
- # set some defaults
- $name ||= '';
- $latitude ||= 0;
- $longitude ||= 0;
-
- my $id = dbh()->selectrow_array("select nextval('problem_id_seq')");
- Utils::workaround_pg_bytea("insert into problem
- (id, postcode, latitude, longitude, title, detail, name,
- email, phone, photo, state, used_map, anonymous, category, areas)
- values
- (?, '', ?, ?, ?, '', ?, ?, '', ?, 'partial', 't', 'f', '', '')", 7,
- $id, $latitude, $longitude, $title, $name, $email, $image
- );
-
- dbh()->do('insert into flickr_imported (id, problem_id) values (?, ?)', {}, $photo_id, $id);
-
- # XXX: Needs to only send email once to user per batch of photos, not one per photo?
- my $template = File::Slurp::read_file("$FindBin::Bin/../templates/emails/flickr-submit");
- my %h = ();
- my $token = mySociety::AuthToken::store('partial', $id);
- $h{name} = $name;
- $h{url} = mySociety::Config::get('BASE_URL') . '/L/' . $token;
-
- my $body = mySociety::Email::construct_email({
- _template_ => $template,
- _parameters_ => \%h,
- To => $name ? [ [ $email, $name ] ] : $email,
- From => [ mySociety::Config::get('CONTACT_EMAIL'), 'FixMyStreet' ],
- });
-
- my $result = mySociety::EmailUtil::send_email($body, mySociety::Config::get('CONTACT_EMAIL'), $email);
- if ($result == mySociety::EmailUtil::EMAIL_SUCCESS) {
- dbh()->commit();
- } else {
- dbh()->rollback();
- }
-}
-
diff --git a/bin/send-alerts b/bin/send-alerts
index c52af4059..89dc18ee7 100755
--- a/bin/send-alerts
+++ b/bin/send-alerts
@@ -5,37 +5,18 @@
#
# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: send-alerts,v 1.5 2010-01-06 16:50:25 louise Exp $
use strict;
require 5.8.0;
use CGI; # XXX
-
-# Horrible boilerplate to set up appropriate library paths.
-use FindBin;
-use lib "$FindBin::Bin/../perllib";
-use lib "$FindBin::Bin/../commonlib/perllib";
use CronFns;
use mySociety::Config;
-use mySociety::DBHandle qw(dbh);
-use FixMyStreet::Alert;
-
-BEGIN {
- mySociety::Config::set_file("$FindBin::Bin/../conf/general");
- mySociety::DBHandle::configure(
- Name => mySociety::Config::get('BCI_DB_NAME'),
- User => mySociety::Config::get('BCI_DB_USER'),
- Password => mySociety::Config::get('BCI_DB_PASS'),
- Host => mySociety::Config::get('BCI_DB_HOST', undef),
- Port => mySociety::Config::get('BCI_DB_PORT', undef)
- );
-}
+use FixMyStreet::App;
my $site = CronFns::site(mySociety::Config::get('BASE_URL'));
CronFns::language($site);
-my $testing_email = mySociety::Config::get('TESTING_EMAIL');
-FixMyStreet::Alert::email_alerts($testing_email);
+
+FixMyStreet::App->model('DB::AlertType')->email_alerts();
diff --git a/bin/send-questionnaires b/bin/send-questionnaires
index d3945bf12..d6e269e32 100755
--- a/bin/send-questionnaires
+++ b/bin/send-questionnaires
@@ -3,115 +3,23 @@
# send-questionnaires:
# Send out creator questionnaires
#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
+# Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved.
# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: send-questionnaires,v 1.21 2010-01-06 14:44:45 louise Exp $
use strict;
require 5.8.0;
-# Horrible boilerplate to set up appropriate library paths.
-use FindBin;
-use lib "$FindBin::Bin/../perllib";
-use lib "$FindBin::Bin/../commonlib/perllib";
-use File::Slurp;
use CGI; # XXX Awkward kludge
use Encode;
use CronFns;
-use Page;
-use mySociety::AuthToken;
+use FixMyStreet::App;
use mySociety::Config;
-use mySociety::DBHandle qw(dbh select_all);
-use mySociety::Email;
-use mySociety::Locale;
-use mySociety::MaPit;
-use mySociety::EmailUtil;
-use mySociety::Random qw(random_bytes);
-
-BEGIN {
- mySociety::Config::set_file("$FindBin::Bin/../conf/general");
- mySociety::DBHandle::configure(
- Name => mySociety::Config::get('BCI_DB_NAME'),
- User => mySociety::Config::get('BCI_DB_USER'),
- Password => mySociety::Config::get('BCI_DB_PASS'),
- Host => mySociety::Config::get('BCI_DB_HOST', undef),
- Port => mySociety::Config::get('BCI_DB_PORT', undef)
- );
-}
-
-my ($verbose, $nomail) = CronFns::options();
-my $site = CronFns::site(mySociety::Config::get('BASE_URL'));
-CronFns::language($site);
-
-# Select all problems that need a questionnaire email sending
-my $unsent = select_all(
- "select id, council, category, title, detail, name, email, cobrand, cobrand_data, lang,
- extract(epoch from ms_current_timestamp()-created) as created
- from problem
- where state in ('confirmed','fixed')
- and whensent is not null
- and whensent < ms_current_timestamp() - '4 weeks'::interval
- and send_questionnaire = 't'
- and ( (select max(whensent) from questionnaire where problem.id=problem_id) is null
- or (select max(whenanswered) from questionnaire where problem.id=problem_id) < ms_current_timestamp() - '4 weeks'::interval)
- order by created desc
-");
-
-foreach my $row (@$unsent) {
- my @all_councils = split /,|\|/, $row->{council};
- my $cobrand = $row->{cobrand};
- my $lang = $row->{lang};
- Cobrand::set_lang_and_domain($cobrand, $lang, 1);
- # Cobranded and non-cobranded messages can share a database. In this case, the conf file
- # should specify a vhost to send the reports for each cobrand, so that they don't get sent
- # more than once if there are multiple vhosts running off the same database. The email_host
- # call checks if this is the host that sends mail for this cobrand.
- next unless (Cobrand::email_host($cobrand));
- my ($councils, $missing) = $row->{council} =~ /^([\d,]+)(?:\|([\d,]+))?/;
- my @councils = split /,/, $councils;
- my $areas_info = mySociety::MaPit::call('areas', \@all_councils);
- my $template = File::Slurp::read_file("$FindBin::Bin/../templates/emails/questionnaire");
-
- my %h = map { $_ => $row->{$_} } qw/name title detail category/;
- $h{created} = Page::prettify_duration($row->{created}, 'week');
- $h{councils} = join(' and ', map { $areas_info->{$_}->{name} } @councils);
-
- my $id = dbh()->selectrow_array("select nextval('questionnaire_id_seq');");
- dbh()->do('insert into questionnaire (id, problem_id, whensent)
- values (?, ?, ms_current_timestamp())', {}, $id, $row->{id});
- dbh()->do("update problem set send_questionnaire = 'f' where id=?", {}, $row->{id});
-
- my $token = mySociety::AuthToken::store('questionnaire', $id);
- $h{url} = Cobrand::base_url_for_emails($cobrand, $row->{cobrand_data}) . '/Q/' . $token;
-
- my $sender = Cobrand::contact_email($cobrand);
- my $sender_name = _(Cobrand::contact_name($cobrand));
- $sender =~ s/team/fms-DO-NOT-REPLY/;
- $template = _($template);
- my $email = mySociety::Locale::in_gb_locale { mySociety::Email::construct_email({
- _template_ => $template,
- _parameters_ => \%h,
- To => [ [ $row->{email}, $row->{name} ] ],
- From => [ $sender, $sender_name ],
- 'Message-ID' => sprintf('<ques-%s-%s@mysociety.org>', time(), unpack('h*', random_bytes(5, 1))),
- }) };
- print "Sending questionnaire $id, problem $row->{id}, token $token to $row->{email}\n" if $verbose;
+my %params;
+( $params{verbose}, $params{nomail} ) = CronFns::options();
+$params{site} = CronFns::site(mySociety::Config::get('BASE_URL'));
+CronFns::language($params{site});
- my $result;
- if ($nomail) {
- $result = -1;
- } else {
- $result = mySociety::EmailUtil::send_email($email, $sender, $row->{email});
- }
- if ($result == mySociety::EmailUtil::EMAIL_SUCCESS) {
- print " ...success\n" if $verbose;
- dbh()->commit();
- } else {
- print " ...failed\n" if $verbose;
- dbh()->rollback();
- }
-}
+FixMyStreet::App->model('DB::Questionnaire')->send_questionnaires( \%params );
diff --git a/bin/send-questionnaires-eha b/bin/send-questionnaires-eha
deleted file mode 100755
index 2e0af8f4f..000000000
--- a/bin/send-questionnaires-eha
+++ /dev/null
@@ -1,114 +0,0 @@
-#!/usr/bin/perl -w
-
-# send-questionnaires-eha:
-# Send out creator questionnaires
-#
-# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: send-questionnaires-eha,v 1.6 2009-09-10 09:10:56 matthew Exp $
-
-use strict;
-require 5.8.0;
-
-use CGI; # XXX
-
-# Horrible boilerplate to set up appropriate library paths.
-use FindBin;
-use lib "$FindBin::Bin/../perllib";
-use lib "$FindBin::Bin/../commonlib/perllib";
-use File::Slurp;
-use CronFns;
-
-use mySociety::AuthToken;
-use mySociety::Config;
-use mySociety::DBHandle qw(dbh select_all);
-use mySociety::Email;
-use mySociety::Locale;
-use mySociety::EmailUtil;
-use mySociety::Random qw(random_bytes);
-
-BEGIN {
- mySociety::Config::set_file("$FindBin::Bin/../conf/general");
- mySociety::DBHandle::configure(
- Name => mySociety::Config::get('BCI_DB_NAME'),
- User => mySociety::Config::get('BCI_DB_USER'),
- Password => mySociety::Config::get('BCI_DB_PASS'),
- Host => mySociety::Config::get('BCI_DB_HOST', undef),
- Port => mySociety::Config::get('BCI_DB_PORT', undef)
- );
-}
-
-# Set up site, language etc.
-my ($verbose, $nomail) = CronFns::options();
-my $site = CronFns::site(mySociety::Config::get('BASE_URL'));
-CronFns::language($site);
-
-send_q('4 weeks');
-send_q('26 weeks');
-
-# ---
-
-sub send_q {
- my ($period) = @_;
-
- (my $template = $period) =~ s/ //;
- $template = File::Slurp::read_file("$FindBin::Bin/../templates/emails/questionnaire-eha-$template");
-
- my $query = "select id, category, title, detail, name, email, lang
- from problem
- where state in ('confirmed', 'fixed')
- and whensent is not null
- and send_questionnaire = 't'
- and whensent < ms_current_timestamp() - '$period'::interval
- and ";
- if ($period eq '4 weeks') {
- $query .= '(select max(whensent) from questionnaire where problem.id=problem_id) is null';
- } else {
- $query .= '(select max(whensent) from questionnaire where problem.id=problem_id) is not null';
- }
- $query .= ' order by created desc';
-
- my $unsent = select_all($query);
- foreach my $row (@$unsent) {
- my %h = map { $_ => $row->{$_} } qw/name title detail category/;
-
- mySociety::Locale::change($row->{lang});
-
- my $id = dbh()->selectrow_array("select nextval('questionnaire_id_seq');");
- dbh()->do('insert into questionnaire (id, problem_id, whensent)
- values (?, ?, ms_current_timestamp())', {}, $id, $row->{id});
- dbh()->do("update problem set send_questionnaire = 'f' where id=?", {}, $row->{id})
- if $period eq '26 weeks';
-
- my $token = mySociety::AuthToken::store('questionnaire', $id);
- $h{url} = mySociety::Config::get('BASE_URL') . '/Q/' . $token;
-
- my $sender = mySociety::Config::get('CONTACT_EMAIL');
- $template = _($template);
- my $email = mySociety::Locale::in_gb_locale { mySociety::Email::construct_email({
- _template_ => $template,
- _parameters_ => \%h,
- To => [ [ $row->{email}, $row->{name} ] ],
- From => [ $sender, _('Report Empty Homes') ],
- 'Message-ID' => sprintf('<ques-%s-%s@emptyhomes.com>', time(), unpack('h*', random_bytes(5, 1))),
- }) };
-
- print "Sending questionnaire $id, problem $row->{id}, token $token to $row->{email}\n" if $verbose;
-
- my $result;
- if ($nomail) {
- $result = -1;
- } else {
- $result = mySociety::EmailUtil::send_email($email, $sender, $row->{email});
- }
- if ($result == mySociety::EmailUtil::EMAIL_SUCCESS) {
- print " ...success\n" if $verbose;
- dbh()->commit();
- } else {
- print " ...failed\n" if $verbose;
- dbh()->rollback();
- }
- }
-}
-
diff --git a/bin/send-reports b/bin/send-reports
index d51276e9d..fbbd3db5e 100755
--- a/bin/send-reports
+++ b/bin/send-reports
@@ -3,18 +3,12 @@
# send-reports:
# Send new problem reports to councils
#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
+# Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved.
# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: send-reports,v 1.79 2010-01-06 16:50:26 louise Exp $
use strict;
require 5.8.0;
-# Horrible boilerplate to set up appropriate library paths.
-use FindBin;
-use lib "$FindBin::Bin/../perllib";
-use lib "$FindBin::Bin/../commonlib/perllib";
use Digest::MD5;
use Encode;
use Error qw(:try);
@@ -25,81 +19,61 @@ use LWP::Simple;
use CGI; # Trying awkward kludge
use CronFns;
+use FixMyStreet::App;
+
use EastHantsWSDL;
-use Cobrand;
use Utils;
use mySociety::Config;
-use mySociety::DBHandle qw(dbh);
-use mySociety::Email;
use mySociety::EmailUtil;
-use mySociety::Locale;
use mySociety::MaPit;
-use mySociety::Random qw(random_bytes);
use mySociety::Web qw(ent);
-BEGIN {
- mySociety::Config::set_file("$FindBin::Bin/../conf/general");
- mySociety::DBHandle::configure(
- Name => mySociety::Config::get('BCI_DB_NAME'),
- User => mySociety::Config::get('BCI_DB_USER'),
- Password => mySociety::Config::get('BCI_DB_PASS'),
- Host => mySociety::Config::get('BCI_DB_HOST', undef),
- Port => mySociety::Config::get('BCI_DB_PORT', undef)
- );
-}
-
# Set up site, language etc.
my ($verbose, $nomail) = CronFns::options();
my $base_url = mySociety::Config::get('BASE_URL');
my $site = CronFns::site($base_url);
-my $query = "SELECT id, council, category, title, detail, name, email, phone,
- used_map, latitude, longitude, (photo is not null) as has_photo, lang,
- cobrand, cobrand_data, date_trunc('second', confirmed) as confirmed, postcode
- FROM problem
- WHERE state in ('confirmed','fixed')
- AND whensent IS NULL
- AND council IS NOT NULL";
-my $unsent = dbh()->selectall_arrayref($query, { Slice => {} });
-
+my $unsent = FixMyStreet::App->model("DB::Problem")->search( {
+ state => [ 'confirmed', 'fixed' ],
+ whensent => undef,
+ council => { '!=', undef },
+} );
my (%notgot, %note);
-my $cobrand;
-my $cobrand_data;
-foreach my $row (@$unsent) {
+while (my $row = $unsent->next) {
- $cobrand = $row->{cobrand};
- $cobrand_data = $row->{cobrand_data};
+ my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($row->cobrand)->new();
+
# Cobranded and non-cobranded messages can share a database. In this case, the conf file
# should specify a vhost to send the reports for each cobrand, so that they don't get sent
# more than once if there are multiple vhosts running off the same database. The email_host
# call checks if this is the host that sends mail for this cobrand.
- next unless (Cobrand::email_host($cobrand));
- Cobrand::set_lang_and_domain($cobrand, $row->{lang}, 1);
- if (dbh()->selectrow_array('select email from abuse where lower(email)=?', {}, lc($row->{email}))) {
- dbh()->do("update problem set state='hidden' where id=?", {}, $row->{id});
- dbh()->commit();
+ next unless $cobrand->email_host();
+ $cobrand->set_lang_and_domain($row->lang, 1);
+ if ( $row->is_from_abuser ) {
+ $row->update( { state => 'hidden' } );
next;
}
my $send_email = 0;
my $send_web = 0;
- mySociety::Locale::change($row->{lang});
-
# Template variables for the email
- my $email_base_url = Cobrand::base_url_for_emails($cobrand, $cobrand_data);
- my %h = map { $_ => $row->{$_} } qw/id title detail name email phone category latitude longitude confirmed used_map/;
- $h{query} = $row->{postcode};
- $h{url} = $email_base_url . '/report/' . $row->{id};
+ my $email_base_url = $cobrand->base_url_for_emails($row->cobrand_data);
+ my %h = map { $_ => $row->$_ } qw/id title detail name category latitude longitude used_map/;
+ map { $h{$_} = $row->user->$_ } qw/email phone/;
+ $h{confirmed} = DateTime::Format::Pg->format_datetime( $row->confirmed->truncate (to => 'second' ) );
+
+ $h{query} = $row->postcode;
+ $h{url} = $email_base_url . '/report/' . $row->id;
$h{phone_line} = $h{phone} ? _('Phone:') . " $h{phone}\n\n" : '';
- if ($row->{has_photo}) {
+ if ($row->photo) {
$h{has_photo} = _("This web page also contains a photo of the problem, provided by the user.") . "\n\n";
- $h{image_url} = $email_base_url . '/photo?id=' . $row->{id};
+ $h{image_url} = $email_base_url . '/photo?id=' . $row->id;
} else {
$h{has_photo} = '';
$h{image_url} = '';
}
- $h{fuzzy} = $row->{used_map} ? _('To view a map of the precise location of this issue')
+ $h{fuzzy} = $row->used_map ? _('To view a map of the precise location of this issue')
: _('The user could not locate the problem on a map, but to see the area around the location they entered');
$h{closest_address} = '';
@@ -116,35 +90,37 @@ foreach my $row (@$unsent) {
$h{closest_address} = find_closest($row, $h{latitude}, $h{longitude});
}
- $h{closest_address_machine} = $h{closest_address};
my (@to, @recips, $template, $areas_info);
if ($site eq 'emptyhomes') {
- my $council = $row->{council};
+ my $council = $row->council;
$areas_info = mySociety::MaPit::call('areas', $council);
my $name = $areas_info->{$council}->{name};
- my ($council_email, $confirmed, $note) = dbh()->selectrow_array(
- "SELECT email,confirmed,note FROM contacts WHERE deleted='f'
- and area_id=? AND category=?", {}, $council, 'Empty property');
+ my $contact = FixMyStreet::App->model("DB::Contact")->find( {
+ deleted => 0,
+ area_id => $council,
+ category => 'Empty property',
+ } );
+ my ($council_email, $confirmed, $note) = ( $contact->email, $contact->confirmed, $contact->note );
unless ($confirmed) {
$note = 'Council ' . $council . ' deleted' unless $note;
$council_email = 'N/A' unless $council_email;
- $notgot{$council_email}{$row->{category}}++;
- $note{$council_email}{$row->{category}} = $note;
+ $notgot{$council_email}{$row->category}++;
+ $note{$council_email}{$row->category} = $note;
next;
}
push @to, [ $council_email, $name ];
@recips = ($council_email);
$send_email = 1;
- $template = File::Slurp::read_file("$FindBin::Bin/../templates/emails/submit-eha");
+ $template = File::Slurp::read_file("$FindBin::Bin/../templates/email/emptyhomes/" . $row->lang . "/submit.txt");
} else {
# XXX Needs locks!
- my @all_councils = split /,|\|/, $row->{council};
- my ($councils, $missing) = $row->{council} =~ /^([\d,]+)(?:\|([\d,]+))?/;
+ my @all_councils = split /,|\|/, $row->council;
+ my ($councils, $missing) = $row->council =~ /^([\d,]+)(?:\|([\d,]+))?/;
my @councils = split(/,/, $councils);
$areas_info = mySociety::MaPit::call('areas', \@all_councils);
my (@dear, %recips);
@@ -158,18 +134,21 @@ foreach my $row (@$unsent) {
} elsif ($areas_info->{$council}->{type} eq 'LBO') { # London
$send_web = 'london';
} else {
- my ($council_email, $confirmed, $note) = dbh()->selectrow_array(
- "SELECT email,confirmed,note FROM contacts WHERE deleted='f'
- and area_id=? AND category=?", {}, $council, $row->{category});
- $council_email = essex_contact($row->{latitude}, $row->{longitude}) if $council == 2225;
- $council_email = oxfordshire_contact($row->{latitude}, $row->{longitude}) if $council == 2237 && $council_email eq 'SPECIAL';
+ my $contact = FixMyStreet::App->model("DB::Contact")->find( {
+ deleted => 0,
+ area_id => $council,
+ category => $row->category
+ } );
+ my ($council_email, $confirmed, $note) = ( $contact->email, $contact->confirmed, $contact->note );
+ $council_email = essex_contact($row->latitude, $row->longitude) if $council == 2225;
+ $council_email = oxfordshire_contact($row->latitude, $row->longitude) if $council == 2237 && $council_email eq 'SPECIAL';
unless ($confirmed) {
$all_confirmed = 0;
- $note = 'Council ' . $row->{council} . ' deleted'
+ $note = 'Council ' . $row->council . ' deleted'
unless $note;
$council_email = 'N/A' unless $council_email;
- $notgot{$council_email}{$row->{category}}++;
- $note{$council_email}{$row->{category}} = $note;
+ $notgot{$council_email}{$row->category}++;
+ $note{$council_email}{$row->category} = $note;
}
push @to, [ $council_email, $name ];
$recips{$council_email} = 1;
@@ -179,9 +158,9 @@ foreach my $row (@$unsent) {
@recips = keys %recips;
next unless $all_confirmed;
- $template = 'submit-council';
- $template = 'submit-brent' if $row->{council} eq 2488 || $row->{council} eq 2237;
- $template = File::Slurp::read_file("$FindBin::Bin/../templates/emails/$template");
+ $template = 'submit.txt';
+ $template = 'submit-brent.txt' if $row->council eq 2488 || $row->council eq 2237;
+ $template = File::Slurp::read_file("$FindBin::Bin/../templates/email/" . $cobrand->moniker . "/$template");
if ($h{category} eq _('Other')) {
$h{category_footer} = _('this type of local problem');
@@ -210,19 +189,16 @@ foreach my $row (@$unsent) {
}
unless ($send_email || $send_web) {
- die 'Report not going anywhere for ID ' . $row->{id} . '!';
+ die 'Report not going anywhere for ID ' . $row->id . '!';
}
- my $testing_email = mySociety::Config::get('TESTING_EMAIL');
- if ($row->{email} eq $testing_email) {
- @recips = ( $testing_email );
- $send_web = 0;
- $send_email = 1;
- } elsif (mySociety::Config::get('STAGING_SITE')) {
+ if (mySociety::Config::get('STAGING_SITE')) {
# on a staging server send emails to ourselves rather than the councils
@recips = ( mySociety::Config::get('CONTACT_EMAIL') );
+ $send_web = 0;
+ $send_email = 1;
} elsif ($site eq 'emptyhomes') {
- my $council = $row->{council};
+ my $council = $row->council;
my $country = $areas_info->{$council}->{country};
if ($country eq 'W') {
push @recips, 'shelter@' . mySociety::Config::get('EMAIL_DOMAIN');
@@ -232,7 +208,7 @@ foreach my $row (@$unsent) {
}
# Special case for this parish council
- # if ($address && $address =~ /Sprowston/ && $row->{council} == 2233 && $row->{category} eq 'Street lighting') {
+ # if ($address && $address =~ /Sprowston/ && $row->council == 2233 && $row->category eq 'Street lighting') {
# $h{councils_name} = 'Sprowston Parish Council';
# my $e = 'parishclerk' . '@' . 'sprowston-pc.gov.uk';
# @to = ( [ $e, $h{councils_name} ] );
@@ -243,19 +219,17 @@ foreach my $row (@$unsent) {
my $result = -1;
if ($send_email) {
- $template = _($template);
- my $email = mySociety::Locale::in_gb_locale { mySociety::Email::construct_email({
- _template_ => $template,
- _parameters_ => \%h,
- To => \@to,
- From => [ $row->{email}, $row->{name} ],
- 'Message-ID' => sprintf('<report-%s-%s@mysociety.org>', time(), unpack('h*', random_bytes(5, 1))),
- }) };
- if (!$nomail) {
- $result *= mySociety::EmailUtil::send_email($email, mySociety::Config::get('CONTACT_EMAIL'), @recips);
- } else {
- print $email;
- }
+ $result *= FixMyStreet::App->send_email_cron(
+ {
+ _template_ => $template,
+ _parameters_ => \%h,
+ To => \@to,
+ From => [ $row->user->email, $row->name ],
+ },
+ mySociety::Config::get('CONTACT_EMAIL'),
+ \@recips,
+ $nomail
+ );
}
if ($send_web eq 'easthants') {
@@ -271,11 +245,10 @@ foreach my $row (@$unsent) {
}
if ($result == mySociety::EmailUtil::EMAIL_SUCCESS) {
- dbh()->do('UPDATE problem SET whensent=ms_current_timestamp(),
- lastupdate=ms_current_timestamp() WHERE id=?', {}, $row->{id});
- dbh()->commit();
- } else {
- dbh()->rollback();
+ $row->update( {
+ whensent => \'ms_current_timestamp()',
+ lastupdate => \'ms_current_timestamp()',
+ } );
}
}
@@ -449,7 +422,7 @@ sub find_closest {
my ($row, $latitude, $longitude) = @_;
my $str = '';
- return '' unless $row->{used_map};
+ return '' unless $row->used_map;
# Get nearest road-type thing from Bing
my $url = "http://dev.virtualearth.net/REST/v1/Locations/$latitude,$longitude?c=en-GB&key=" . mySociety::Config::get('BING_MAPS_API_KEY');
diff --git a/conf/.gitignore b/conf/.gitignore
index 10f651513..0a78c8825 100644
--- a/conf/.gitignore
+++ b/conf/.gitignore
@@ -1,2 +1,4 @@
/general
/general.deployed
+/httpd.conf
+/httpd.conf.deployed
diff --git a/conf/cobrands/.gitignore b/conf/cobrands/.gitignore
deleted file mode 100644
index 911c95650..000000000
--- a/conf/cobrands/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/cities \ No newline at end of file
diff --git a/conf/crontab.ugly b/conf/crontab.ugly
index 5e65e1cee..a5a5f2300 100644
--- a/conf/crontab.ugly
+++ b/conf/crontab.ugly
@@ -12,24 +12,18 @@ MAILTO=cron-!!(*= $site *)!!@mysociety.org
# On only one server
!!(* if ($vhost eq 'reportemptyhomes.com') { *)!!
-5,10,15,20,25,30,35,40,45,50,55 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-reports.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/send-reports || echo "stalled?"
-0 0-11,13-23 * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-reports.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/send-reports || echo "stalled?"
-0 12 * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-reports.lock "/data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/send-reports --verbose" || echo "stalled?"
-2 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-alerts.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/send-alerts || echo "stalled?"
-0,30 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-questionnaires.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/send-questionnaires-eha || echo "stalled?"
-
-!!(* } elsif ($vhost eq 'cities.fixmystreet.com') { *)!!
-2,7,12,17,22,27,32,37,42,47,52,57 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-reports.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/send-reports || echo "stalled?"
-33 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-alerts.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/send-alerts || echo "stalled?"
-5,35 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-questionnaires.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/send-questionnaires || echo "stalled?"
+5,10,15,20,25,30,35,40,45,50,55 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-reports.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/cron-wrapper send-reports || echo "stalled?"
+0 0-11,13-23 * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-reports.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/cron-wrapper send-reports || echo "stalled?"
+0 12 * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-reports.lock "/data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/cron-wrapper send-reports --verbose" || echo "stalled?"
+2 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-alerts.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/cron-wrapper send-alerts || echo "stalled?"
+0,30 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-questionnaires.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/cron-wrapper send-questionnaires || echo "stalled?"
!!(* } elsif (($vhost eq 'www.fixmystreet.com') || ($vhost eq 'matthew.fixmystreet.com')) { *)!!
-5,10,15,20,25,30,35,40,45,50,55 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-reports.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/send-reports || echo "stalled?"
-0 0-8,10,11,13,14,16,17,19-23 * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-reports.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/send-reports || echo "stalled?"
-0 9,12,15,18 * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-reports.lock "/data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/send-reports --verbose" || echo "stalled?"
-15,45 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/import-flickr.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/import-flickr || echo "stalled?"
-2 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-alerts.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/send-alerts || echo "stalled?"
-0,30 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-questionnaires.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/send-questionnaires || echo "stalled?"
+5,10,15,20,25,30,35,40,45,50,55 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-reports.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/cron-wrapper send-reports || echo "stalled?"
+0 0-8,10,11,13,14,16,17,19-23 * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-reports.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/cron-wrapper send-reports || echo "stalled?"
+0 9,12,15,18 * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-reports.lock "/data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/cron-wrapper send-reports --verbose" || echo "stalled?"
+2 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-alerts.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/cron-wrapper send-alerts || echo "stalled?"
+0,30 * * * * !!(*= $user *)!! run-with-lockfile -n /data/vhost/!!(*= $vhost *)!!/send-questionnaires.lock /data/vhost/!!(*= $vhost *)!!/fixmystreet/bin/cron-wrapper send-questionnaires || echo "stalled?"
!!(* } *)!!
diff --git a/conf/general-example b/conf/general-example
index 7e750de96..6401528a9 100644
--- a/conf/general-example
+++ b/conf/general-example
@@ -30,11 +30,11 @@ define('OPTION_BASE_URL', 'http://www.example.org');
# Which country are you operating in? ISO3166-alpha2 code please
define('OPTION_COUNTRY', 'GB');
-define('OPTION_TESTING_EMAIL', 'testing@example.com');
define('OPTION_EMAIL_DOMAIN', 'example.org');
define('OPTION_CONTACT_EMAIL', 'team@'.OPTION_EMAIL_DOMAIN);
define('OPTION_TEST_EMAIL_PREFIX', null);
-define('OPTION_EMAIL_VHOST_CITIES', 'www.example.com');
+
+# this should be the same as the OPTION_BASE_URL for historical reasons
define('OPTION_EMAIL_VHOST', 'www.example.com');
define('OPTION_EMAIL_VHOST_EMPTYHOMES', 'www.example.com');
@@ -65,8 +65,6 @@ define('OPTION_HEARFROMYOURMP_BASE_URL', '');
define('OPTION_SMTP_SMARTHOST', 'localhost');
-define('OPTION_FLICKR_API', '');
-
define('OPTION_IPHONE_URL', '');
// Log file (used in test harness, and later in admin scripts)
diff --git a/conf/httpd.conf b/conf/httpd.conf
deleted file mode 100644
index d9d564f79..000000000
--- a/conf/httpd.conf
+++ /dev/null
@@ -1,105 +0,0 @@
-# Apache configuration for FixMyStreet.
-#
-# Add lines something like this to your /etc/apache2/sites-enabled/fixmystreet -
-# replacing '/home/yourname/fixmystreet' with the path to your install
-#
-# # FixMyStreet
-# <VirtualHost *:80>
-# ServerName fixmystreet.yourservername
-# DocumentRoot /home/yourname/fixmystreet/web/
-#
-# # Pull in the specific config
-# Include /home/yourname/fixmystreet/conf/httpd.conf
-#
-# <Directory /home/yourname/fixmystreet/web>
-# # You also need to enable cgi files to run as CGI scripts. For example:
-# # on production servers these are run under fastcgi
-# Options +ExecCGI
-# AddHandler cgi-script .cgi
-# </Directory>
-#
-# <Directory /home/yourname/fixmystreet/web-admin>
-# #
-# # WARNING - enable auth here on production machine
-# #
-# Options +ExecCGI
-# AddHandler cgi-script .cgi
-# </Directory>
-#
-# Alias /admin/ /home/yourname/fixmystreet/web-admin/
-#
-# Alias /jslib/ "/home/yourname/fixmystreet/commonlib/jslib/"
-#
-# </VirtualHost>
-#
-#
-# Copyright (c) 2006 UK Citizens Online Democracy. All rights reserved. Email:
-# francis@mysociety.org; WWW: http://www.mysociety.org
-#
-# $Id: httpd.conf,v 1.40 2009-10-21 15:22:36 louise Exp $
-
-DirectoryIndex index.cgi
-
-RewriteEngine on
-#RewriteLog /var/log/apache2/rewrite.log
-#RewriteLogLevel 3
-
-# End slashes goodbye
-RewriteRule ^/admin/ - [L]
-RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
-RewriteRule ^(.+)/$ $1 [R=permanent]
-
-# Confirmation tokens
-RewriteRule ^/[Aa]/([0-9A-Za-z]{16,18}).*$ /alert.cgi?token=$1 [QSA]
-RewriteRule ^/[Cc]/([0-9A-Za-z]{16,18}).*$ /confirm.cgi?type=update;token=$1 [QSA]
-RewriteRule ^/[Pp]/([0-9A-Za-z]{16,18}).*$ /confirm.cgi?type=problem;token=$1 [QSA]
-RewriteRule ^/[Qq]/([0-9A-Za-z]{16,18}).*$ /questionnaire.cgi?token=$1 [QSA]
-RewriteRule ^/[Ff]/([0-9A-Za-z]{16,18}).*$ /flickr.cgi?token=$1
-RewriteRule ^/[Ll]/([0-9A-Za-z]{16,18}).*$ /index.cgi?partial_token=$1
-RewriteRule ^/[Tt]/([0-9A-Za-z]{16,18}).*$ /tms-signup.cgi?token=$1
-
-# RSS feeds for updates on a problem
-RewriteRule ^/rss/([0-9]+)$ /rss.cgi?type=new_updates;id=$1 [QSA]
-
-# RSS feeds for new local problems
-RewriteRule ^/rss/([0-9]+)[,/]([0-9]+)$ /rss.cgi?type=local_problems;x=$1;y=$2 [QSA]
-RewriteRule ^/rss/n/([0-9]+)[,/]([0-9]+)$ /rss.cgi?type=local_problems;e=$1;n=$2 [QSA]
-RewriteRule ^/rss/l/([0-9.-]+)[,/]([0-9.-]+)$ /rss.cgi?type=local_problems;lat=$1;lon=$2 [QSA]
-RewriteRule ^/rss/([0-9]+)[,/]([0-9]+)/([0-9]+)$ /rss.cgi?type=local_problems;x=$1;y=$2;d=$3 [QSA]
-RewriteRule ^/rss/n/([0-9]+)[,/]([0-9]+)/([0-9]+)$ /rss.cgi?type=local_problems;e=$1;n=$2;d=$3 [QSA]
-RewriteRule ^/rss/l/([0-9.-]+)[,/]([0-9.-]+)/([0-9]+)$ /rss.cgi?type=local_problems;lat=$1;lon=$2;d=$3 [QSA]
-RewriteRule ^/rss/pc/(.*)/([0-9]+)$ /rss.cgi?type=local_problems;pc=$1;d=$2 [QSA]
-RewriteRule ^/rss/pc/(.*)$ /rss.cgi?type=local_problems;pc=$1 [QSA]
-RewriteRule ^/rss/problems$ /rss.cgi?type=new_problems [QSA]
-
-# RSS feeds for voting areas
-RewriteRule ^/rss/council/([0-9]+)$ /rss/reports/$1 [R=permanent]
-RewriteRule ^/report$ /reports [R=permanent]
-RewriteRule ^/reports/([^/]+)/all$ /reports.cgi?council=$1;all=1 [QSA]
-RewriteRule ^/reports/([^/]+)/([^/]+)$ /reports.cgi?council=$1;ward=$2 [QSA]
-RewriteRule ^/rss/(reports|area)/([^/]+)/([^/]+)$ /reports.cgi?rss=$1;council=$2;ward=$3 [QSA]
-RewriteRule ^/reports/([^/]+)$ /reports.cgi?council=$1 [QSA]
-RewriteRule ^/rss/area/([0-9]+)$ /rss.cgi?type=area_problems;id=$1 [QSA]
-RewriteRule ^/rss/(reports|area)/([^/]+)$ /reports.cgi?rss=$1;council=$2 [QSA]
-
-# Fix incorrect RSS urls caused by my stupidity
-RewriteRule ^/{/rss/(.*)}$ /rss/$1 [R=permanent]
-RewriteRule ^/reports/{/rss/(.*)}$ /rss/$1 [R=permanent]
-
-RewriteRule ^/report/([0-9]+)$ /index.cgi?id=$1 [QSA]
-RewriteRule ^/report/([0-9]+) /report/$1 [R]
-RewriteRule ^/alerts/?$ /alert [R=permanent]
-
-# JSON API for summaries of reports
-RewriteRule ^/json/problems/new$ /json.cgi?type=new_problems [QSA]
-RewriteRule ^/json/problems/fixed$ /json.cgi?type=fixed_problems [QSA]
-
-ProxyPass /tilma/ http://tilma.mysociety.org/
-ProxyPassReverse /tilma/ http://tilma.mysociety.org/
-
-# CGI files can be referred without CGI
-RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.cgi -f
-RewriteRule /(.+) /$1.cgi
-
-# S. Cambs
-RewriteRule ^/images/southcambridgeshiredistrictcouncil/icons/internet/print.gif$ http://www.scambs.gov.uk/images/southcambridgeshiredistrictcouncil/icons/internet/print.gif [R=permanent]
diff --git a/conf/httpd.conf-example b/conf/httpd.conf-example
new file mode 100644
index 000000000..54c4b90ac
--- /dev/null
+++ b/conf/httpd.conf-example
@@ -0,0 +1,68 @@
+# Apache configuration for FixMyStreet.
+#
+# Add lines something like this to your /etc/apache2/sites-enabled/fixmystreet -
+# replacing '/home/yourname/fixmystreet' with the path to your install
+#
+# # FixMyStreet
+# <VirtualHost *:80>
+# ServerName fixmystreet.yourservername
+# DocumentRoot /home/yourname/fixmystreet/web/
+#
+# # Pull in the specific config
+# Include /home/yourname/fixmystreet/conf/httpd.conf
+#
+# <Directory /home/yourname/fixmystreet/web>
+# # You also need to enable cgi files to run as CGI scripts. For example:
+# # on production servers these are run under fastcgi
+# Options +ExecCGI
+# AddHandler cgi-script .cgi
+# </Directory>
+#
+# <Directory /home/yourname/fixmystreet/web-admin>
+# #
+# # WARNING - enable auth here on production machine
+# #
+# Options +ExecCGI
+# AddHandler cgi-script .cgi
+# </Directory>
+#
+# Alias /admin/ /home/yourname/fixmystreet/web-admin/
+#
+# Alias /jslib/ "/home/yourname/fixmystreet/commonlib/jslib/"
+#
+# </VirtualHost>
+#
+#
+# Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved.
+# Email: team@mysociety.org
+# WWW: http://www.mysociety.org
+
+RewriteEngine on
+# RewriteLog /var/log/apache2/rewrite.log
+# RewriteLogLevel 3
+
+# RSS feeds for voting areas
+RewriteRule ^/rss/council/([0-9]+)$ /rss/reports/$1 [R=permanent,L]
+RewriteRule ^/report$ /reports [R=permanent,L]
+# Fix incorrect RSS urls caused by my stupidity
+RewriteRule ^/{/rss/(.*)}$ /rss/$1 [R=permanent,L]
+RewriteRule ^/reports/{/rss/(.*)}$ /rss/$1 [R=permanent,L]
+# In case of misspelling
+RewriteRule ^/alerts/?$ /alert [R=permanent,L]
+
+# Proxy tilma so that our js code can make calls on the originating server. Use
+# a RewriteRule rather than ProxyPass so that Apache's processing order is more
+# predictable. ProxyPassReverse is not affected by this.
+RewriteRule /tilma/(.*) http://tilma.mysociety.org/$1 [P,L]
+ProxyPassReverse /tilma/ http://tilma.mysociety.org/
+
+# serve static files directly
+RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f
+RewriteRule /(.+) /$1 [L]
+
+# Don't want jslib being passed to Catalyst
+RewriteRule ^/jslib(.*) /jslib$1 [L,PT]
+
+# trap anything that reaches us here and send it to the Catalyst app
+RewriteRule ^(.*)$ /fixmystreet_app_fastcgi.cgi$1 [L]
+
diff --git a/conf/packages b/conf/packages
index b8324300d..ecd6c18bc 100644
--- a/conf/packages
+++ b/conf/packages
@@ -28,4 +28,8 @@ libmath-bigint-gmp-perl
libtext-template-perl
gettext
libtest-exception-perl
+libipc-run3-perl
+libyaml-perl
+liblist-moreutils-perl
libhaml-ruby
+libtemplate-perl
diff --git a/db/alert_types.pl b/db/alert_types.pl
index fd1243982..3d1d131dc 100644
--- a/db/alert_types.pl
+++ b/db/alert_types.pl
@@ -27,7 +27,7 @@
_('The latest local problems reported by users'),
# New problems around a postcode with a particular state
- _('New problems NEAR {{POSTCODE}} on FixMyStreet'),
+ _('New problems near {{POSTCODE}} on FixMyStreet'),
_('The latest local problems reported by users'),
# New problems sent to a particular council
@@ -35,10 +35,10 @@
_('The latest problems for {{COUNCIL}} reported by users'),
# New problems within a particular ward sent to a particular council
- _('New problems for {{COUNCIL}} within {{WARD}} ward on FixMyStreet'
+ _('New problems for {{COUNCIL}} within {{WARD}} ward on FixMyStreet'),
_('The latest problems for {{COUNCIL}} within {{WARD}} ward reported by users'),
# New problems within a particular voting area (ward, constituency, whatever)
- _('New problems within {{NAME}}''s boundary on FixMyStreet'),
- _('The latest problems within {{NAME}}''s boundary reported by users'),
+ _('New problems within {{NAME}}\'s boundary on FixMyStreet'),
+ _('The latest problems within {{NAME}}\'s boundary reported by users'),
diff --git a/db/alert_types.sql b/db/alert_types.sql
index fb1aac1d0..eca83bb6f 100644
--- a/db/alert_types.sql
+++ b/db/alert_types.sql
@@ -71,7 +71,7 @@ insert into alert_type
item_table, item_where, item_order,
item_title, item_link, item_description, template)
values ('postcode_local_problems_state', '', '',
- 'New problems NEAR {{POSTCODE}} on FixMyStreet', '/', 'The latest local problems reported by users',
+ 'New problems near {{POSTCODE}} on FixMyStreet', '/', 'The latest local problems reported by users',
'problem_find_nearby(?, ?, ?) as nearby,problem', 'nearby.problem_id = problem.id and problem.state in (?)', 'created desc',
'{{title}}, {{confirmed}}', '/report/{{id}}', '{{detail}}', 'alert-problem-nearby');
diff --git a/db/rerun_dbic_loader.pl b/db/rerun_dbic_loader.pl
new file mode 100755
index 000000000..1261f2378
--- /dev/null
+++ b/db/rerun_dbic_loader.pl
@@ -0,0 +1,38 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+# This script inspects the current state of the database and then amends the
+# FixMyStreet::DB::Result::* files to suit. After running the changes should be
+# inspected before the code is commited.
+
+use FixMyStreet;
+use DBIx::Class::Schema::Loader qw/ make_schema_at /;
+
+# create a exclude statement that filters out the table that we are not
+# interested in
+my @tables_to_ignore = (
+ 'debugdate', #
+ 'flickr_imported', #
+ 'partial_user', #
+ 'textmystreet', #
+);
+my $exclude = '^(?:' . join( '|', @tables_to_ignore ) . ')$';
+
+make_schema_at(
+ 'FixMyStreet::DB',
+ {
+ debug => 0, # switch on to be chatty
+ dump_directory => './perllib', # edit files in place
+ exclude => qr{$exclude}, # ignore some tables
+ generate_pod => 0, # no need for pod
+ overwrite_modifications => 1, # don't worry that the md5 is wrong
+
+ # add in some extra components
+ components => [ 'FilterColumn', 'InflateColumn::DateTime' ],
+
+ },
+ FixMyStreet->dbic_connect_info(),
+);
+
diff --git a/db/schema.sql b/db/schema.sql
index c58abaf48..9c5b3d8fd 100644
--- a/db/schema.sql
+++ b/db/schema.sql
@@ -66,6 +66,7 @@ create function ms_current_timestamp()
-- Who to send problems for a specific MaPit area ID to
create table contacts (
+ id serial primary key,
area_id integer not null,
category text not null default 'Other',
email text not null,
@@ -86,6 +87,7 @@ create unique index contacts_area_id_category_idx on contacts(area_id, category)
create table contacts_history (
contacts_history_id serial not null primary key,
+ contact_id integer not null,
area_id integer not null,
category text not null default 'Other',
email text not null,
@@ -105,7 +107,7 @@ create table contacts_history (
create function contacts_updated()
returns trigger as '
begin
- insert into contacts_history (area_id, category, email, editor, whenedited, note, confirmed, deleted) values (new.area_id, new.category, new.email, new.editor, new.whenedited, new.note, new.confirmed, new.deleted);
+ insert into contacts_history (contact_id, area_id, category, email, editor, whenedited, note, confirmed, deleted) values (new.id, new.area_id, new.category, new.email, new.editor, new.whenedited, new.note, new.confirmed, new.deleted);
return new;
end;
' language 'plpgsql';
@@ -115,6 +117,22 @@ create trigger contacts_update_trigger after update on contacts
create trigger contacts_insert_trigger after insert on contacts
for each row execute procedure contacts_updated();
+-- table for sessions - needed by Catalyst::Plugin::Session::Store::DBIC
+create table sessions (
+ id char(72) primary key,
+ session_data text,
+ expires integer
+);
+
+-- users table
+create table users (
+ id serial not null primary key,
+ email text not null unique,
+ name text,
+ phone text,
+ password text not null default ''
+);
+
-- Problems reported by users of site
create table problem (
id serial not null primary key,
@@ -132,9 +150,8 @@ create table problem (
used_map boolean not null,
-- User's details
+ user_id int references users(id) not null,
name text not null,
- email text not null,
- phone text not null,
anonymous boolean not null,
-- External information
@@ -161,6 +178,7 @@ create table problem (
send_questionnaire boolean not null default 't'
);
create index problem_state_latitude_longitude_idx on problem(state, latitude, longitude);
+create index problem_user_id_idx on problem ( user_id );
create table questionnaire (
id serial not null primary key,
@@ -244,8 +262,9 @@ create function problem_find_nearby(double precision, double precision, double p
create table comment (
id serial not null primary key,
problem_id integer not null references problem(id),
+ user_id int references users(id) not null,
+ anonymous bool not null,
name text, -- null means anonymous
- email text not null,
website text,
created timestamp not null default ms_current_timestamp(),
confirmed timestamp,
@@ -265,6 +284,7 @@ create table comment (
-- and should be highlighted in the display?
);
+create index comment_user_id_idx on comment(user_id);
create index comment_problem_id_idx on comment(problem_id);
create index comment_problem_id_created_idx on comment(problem_id, created);
@@ -300,7 +320,7 @@ create table alert (
alert_type text not null references alert_type(ref),
parameter text, -- e.g. Problem ID for new updates, Longitude for local problem alerts
parameter2 text, -- e.g. Latitude for local problem alerts
- email text not null,
+ user_id int references users(id) not null,
confirmed integer not null default 0,
lang text not null default 'en-gb',
cobrand text not null default '' check (cobrand ~* '^[a-z0-9]*$'),
@@ -308,11 +328,11 @@ create table alert (
whensubscribed timestamp not null default ms_current_timestamp(),
whendisabled timestamp default null
);
-create index alert_email_idx on alert(email);
+create index alert_user_id_idx on alert ( user_id );
create index alert_alert_type_confirmed_whendisabled_idx on alert(alert_type, confirmed, whendisabled);
create index alert_whendisabled_cobrand_idx on alert(whendisabled, cobrand);
create index alert_whensubscribed_confirmed_cobrand_idx on alert(whensubscribed, confirmed, cobrand);
--- Possible indexes - unique (alert_type,email,parameter)
+-- Possible indexes - unique (alert_type,user_id,parameter)
create table alert_sent (
alert_id integer not null references alert(id),
@@ -340,9 +360,8 @@ create table flickr_imported (
create unique index flickr_imported_id_idx on flickr_imported(id);
create table abuse (
- email text not null
+ email text primary key check( lower(email) = email )
);
-create unique index abuse_email_idx on abuse(lower(email));
create table textmystreet (
name text not null,
@@ -367,3 +386,4 @@ create table admin_log (
or action = 'resend'),
whenedited timestamp not null default ms_current_timestamp()
);
+
diff --git a/db/schema_0001-add_sessions_and_users_and_dbic_fields.sql b/db/schema_0001-add_sessions_and_users_and_dbic_fields.sql
new file mode 100644
index 000000000..581741fe8
--- /dev/null
+++ b/db/schema_0001-add_sessions_and_users_and_dbic_fields.sql
@@ -0,0 +1,54 @@
+-- These are changes needed to the schema to support moving over to DBIx::Class
+
+begin;
+
+-- table for sessions - needed by Catalyst::Plugin::Session::Store::DBIC
+CREATE TABLE sessions (
+ id CHAR(72) PRIMARY KEY,
+ session_data TEXT,
+ expires INTEGER
+);
+
+-- users table
+create table users (
+ id serial not null primary key,
+ email text not null unique,
+ name text,
+ phone text,
+ password text not null default ''
+);
+
+--- add PK to contacts table
+ALTER TABLE contacts
+ ADD COLUMN id SERIAL PRIMARY KEY;
+
+AlTER TABLE contacts_history
+ ADD COLUMN contact_id integer;
+
+update contacts_history
+ set contact_id = (
+ select id
+ from contacts
+ where contacts_history.category = contacts.category
+ and contacts_history.area_id = contacts.area_id
+ );
+
+AlTER TABLE contacts_history
+ alter COLUMN contact_id SET NOT NULL;
+
+create or replace function contacts_updated()
+ returns trigger as '
+ begin
+ insert into contacts_history (contact_id, area_id, category, email, editor, whenedited, note, confirmed, deleted) values (new.id, new.area_id, new.category, new.email, new.editor, new.whenedited, new.note, new.confirmed, new.deleted);
+ return new;
+ end;
+' language 'plpgsql';
+
+
+--- add pk and lowercase check to abuse
+drop index abuse_email_idx;
+update abuse set email = lower(email);
+alter table abuse add check( lower(email) = email );
+alter table abuse add primary key(email);
+
+commit;
diff --git a/db/schema_0002-create_users_from_problems_and_link.sql b/db/schema_0002-create_users_from_problems_and_link.sql
new file mode 100644
index 000000000..bfb502a96
--- /dev/null
+++ b/db/schema_0002-create_users_from_problems_and_link.sql
@@ -0,0 +1,53 @@
+-- link the problems to the users, creating the users as required. Removes the
+-- email field from the problems.
+--
+-- Uses the most recently used non-anonymmous name as the name for the user.
+
+begin;
+
+-- create users from the problems
+INSERT INTO users (email)
+ SELECT distinct( lower( email ) ) FROM problem;
+
+-- add a user id to the problems
+ALTER TABLE problem
+ ADD COLUMN user_id INT REFERENCES users(id);
+
+-- populate the user_ids
+update problem
+ set user_id = (
+ select id
+ from users
+ where users.email = lower( problem.email )
+ );
+
+-- create the index now that the entries have been made
+create index problem_user_id_idx on problem ( user_id );
+
+-- add names from the problems
+UPDATE users
+ SET name = (
+ select name from problem
+ where user_id = users.id
+ order by created desc
+ limit 1
+ ),
+ phone = (
+ select phone from problem
+ where user_id = users.id
+ order by created desc
+ limit 1
+ );
+
+
+-- make the problems user id not null etc
+ALTER TABLE problem
+ ALTER COLUMN user_id SET NOT NULL;
+
+-- drop emails from the problems
+ALTER TABLE problem
+ DROP COLUMN email;
+ALTER TABLE problem
+ DROP COLUMN phone;
+
+commit;
diff --git a/db/schema_0003-create_users_from_alerts_and_link.sql b/db/schema_0003-create_users_from_alerts_and_link.sql
new file mode 100644
index 000000000..6861e5340
--- /dev/null
+++ b/db/schema_0003-create_users_from_alerts_and_link.sql
@@ -0,0 +1,29 @@
+begin;
+
+-- create any users that don't already exist
+INSERT INTO users (email)
+ SELECT distinct( lower( alert.email ) ) FROM alert
+ LEFT JOIN users ON lower( users.email ) = lower( alert.email )
+ WHERE users.id IS NULL;
+
+ALTER table alert
+ ADD COLUMN user_id INT REFERENCES users(id);
+
+-- populate the user ids in the alert table
+UPDATE alert
+ SET user_id = (
+ SELECT id
+ FROM users
+ WHERE users.email = lower( alert.email )
+ );
+
+CREATE INDEX alert_user_id_idx on alert ( user_id );
+
+-- tidy up now everythings in place
+ALTER table alert
+ ALTER COLUMN user_id SET NOT NULL;
+
+ALTER table alert
+ DROP COLUMN email;
+
+commit;
diff --git a/db/schema_0004-create_users_from_comments_and_link.sql b/db/schema_0004-create_users_from_comments_and_link.sql
new file mode 100644
index 000000000..0e4e2b5f2
--- /dev/null
+++ b/db/schema_0004-create_users_from_comments_and_link.sql
@@ -0,0 +1,49 @@
+begin;
+
+-- create any users that don't already exist
+INSERT INTO users (email)
+ SELECT distinct( lower( comment.email ) ) FROM comment
+ LEFT JOIN users ON lower( users.email ) = lower( comment.email )
+ WHERE users.id IS NULL;
+
+ALTER table comment
+ ADD COLUMN user_id INT REFERENCES users(id);
+
+-- populate the user ids in the comment table
+UPDATE comment
+ SET user_id = (
+ SELECT id
+ FROM users
+ WHERE users.email = lower( comment.email )
+ );
+
+CREATE INDEX comment_user_id_idx on comment ( user_id );
+
+UPDATE users
+ SET name = (
+ select name from comment
+ where user_id = users.id
+ order by created desc
+ limit 1
+ )
+WHERE users.name = '';
+
+-- set up the anonymous flag
+ALTER table comment
+ ADD COLUMN anonymous BOOL;
+
+UPDATE comment SET anonymous = false WHERE name <> '';
+
+UPDATE comment SET anonymous = true WHERE anonymous is NULL;
+
+-- tidy up now everythings in place
+ALTER table comment
+ ALTER COLUMN user_id SET NOT NULL;
+
+ALTER table comment
+ ALTER COLUMN anonymous SET NOT NULL;
+
+ALTER table comment
+ DROP COLUMN email;
+
+commit;
diff --git a/locale/FixMyStreet.po b/locale/FixMyStreet.po
index 87b56efd8..c8448514e 100644
--- a/locale/FixMyStreet.po
+++ b/locale/FixMyStreet.po
@@ -1,14 +1,14 @@
# FixMyStreet original .po file, autogenerated by gettext-extract.
-# Copyright (C) 2008 UK Citizens Online Democracy
+# Copyright (C) 2011 UK Citizens Online Democracy
# This file is distributed under the same license as the main FixMyStreet code.
-# Matthew Somerville <matthew@mysociety.org>, 2008-04-15.
+# Matthew Somerville <matthew@mysociety.org>, 2011-06-03.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: 1.0\n"
"Report-Msgid-Bugs-To: matthew@mysociety.org\n"
-"POT-Creation-Date: 2011-05-04 19:21+0100\n"
+"POT-Creation-Date: 2011-06-09 18:49+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <team@fixmystreet.com>\n"
@@ -17,2890 +17,2007 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
-#: perllib/Cobrands/Emptyhomes/Util.pm:61 bin/send-questionnaires-eha:93
-msgid "Report Empty Homes"
+#: bin/send-reports:173
+msgid " and "
msgstr ""
-#: perllib/Cobrands/Fiksgatami/Util.pm:37
-msgid "Enter a nearby postcode, or street name and area"
+#: templates/web/default/tokens/confirm_problem.html:10 templates/web/default/tokens/confirm_problem.html:6
+msgid " and <strong>we will now send it to the council</strong>"
msgstr ""
-#: perllib/Page.pm:86
-#, perl-format
-msgid ""
-"Please try again later, or <a href=\"mailto:%s\">email us</a> to let us know."
+#: templates/web/default/report/new/all_councils_text.html:10 templates/web/default/report/new/all_councils_text.html:3 templates/web/default/report/new/no_councils_text.html:15 templates/web/default/report/new/no_councils_text.html:3 templates/web/default/report/new/some_councils_text.html:20 templates/web/default/report/new/some_councils_text.html:23 templates/web/default/report/new/some_councils_text.html:5 templates/web/emptyhomes/report/new/all_councils_text.html:2
+msgid " or "
msgstr ""
-#: perllib/Page.pm:87
-msgid "Sorry! Something's gone wrong."
+#: templates/web/default/admin/council_list.html:17
+msgid "%d addresses"
msgstr ""
-#: perllib/Page.pm:88
-msgid "The text of the error was:"
+#: templates/web/default/admin/index.html:16
+msgid "%d confirmed alerts, %d unconfirmed"
msgstr ""
-#: perllib/Page.pm:197 web/contact.cgi:107
-msgid "FixMyStreet"
+#: templates/web/default/admin/index.html:18
+msgid "%d council contacts &ndash; %d confirmed, %d unconfirmed"
msgstr ""
-#: perllib/Page.pm:200 perllib/Page.pm:324
-msgid "Report a problem"
+#: perllib/Utils.pm:245
+msgid "%d day"
msgstr ""
-#: perllib/Page.pm:201 perllib/Page.pm:325
-msgid "All reports"
+#: perllib/Utils.pm:245
+msgid "%d days"
msgstr ""
-#: perllib/Page.pm:202 perllib/Page.pm:326
-msgid "Local alerts"
+#: templates/web/default/admin/council_list.html:27
+msgid "%d edits by %s"
msgstr ""
-#: perllib/Page.pm:203 perllib/Page.pm:327
-msgid "Help"
+#: perllib/Utils.pm:246
+msgid "%d hour"
msgstr ""
-#: perllib/Page.pm:204 web/about.cgi:19 web/about.cgi:21
-msgid "About us"
+#: perllib/Utils.pm:246
+msgid "%d hours"
msgstr ""
-#: perllib/Page.pm:225
-msgid "Fix<span id=\"my\">My</span>Street"
+#: templates/web/default/admin/index.html:15
+msgid "%d live updates"
msgstr ""
-#: perllib/Page.pm:303
-msgid ""
-"This is a developer site; things might break at any time, and the database "
-"will be periodically deleted."
+#: perllib/Utils.pm:247
+msgid "%d minute"
msgstr ""
-#: perllib/Page.pm:317
-msgid ""
-"Built by <a href=\"http://www.mysociety.org/\">mySociety</a>, using some <a "
-"href=\"http://github.com/mysociety/fixmystreet\">clever</a>&nbsp;<a href="
-"\"https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa"
-"\">code</a>."
+#: perllib/Utils.pm:247
+msgid "%d minutes"
msgstr ""
-#: perllib/Page.pm:319
-msgid ""
-"Built by <a href=\"http://www.mysociety.org/\">mySociety</a> and maintained "
-"by <a href=\"http://www.nuug.no/\">NUUG</a>, using some <a href=\"http://"
-"github.com/mysociety/fixmystreet\">clever</a>&nbsp;<a href=\"https://secure."
-"mysociety.org/cvstrac/dir?d=mysociety/services/TilMa\">code</a>."
+#: templates/web/default/admin/index.html:17
+msgid "%d questionnaires sent &ndash; %d answered (%s%%)"
msgstr ""
-#: perllib/Page.pm:323
-msgid "Navigation"
+#: perllib/Utils.pm:244
+msgid "%d week"
msgstr ""
-#: perllib/Page.pm:328
-msgid "Contact"
+#: perllib/Utils.pm:244
+msgid "%d weeks"
msgstr ""
-#: perllib/Page.pm:330
-msgid ""
-"<a href=\"http://www.mysociety.org/\"><img id=\"logo\" width=\"133\" height="
-"\"26\" src=\"/i/mysociety-dark.png\" alt=\"View mySociety.org\"><span id="
-"\"logoie\"></span></a>"
+#: templates/web/default/reports/council.html:11 templates/web/default/reports/council.html:12
+msgid "%s - Summary reports"
msgstr ""
-#: perllib/Page.pm:388
-msgid "Error"
+#: perllib/FixMyStreet/Cobrand/Default.pm:738 perllib/FixMyStreet/Cobrand/Default.pm:752
+msgid "%s ward, %s"
msgstr ""
-#: perllib/Page.pm:461
-msgid "your problem will not be posted"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:307
+msgid "%s, reported anonymously at %s"
msgstr ""
-#: perllib/Page.pm:462
-msgid "we'll hang on to your problem report while you're checking your email."
+#: perllib/FixMyStreet/DB/Result/Problem.pm:309
+msgid "%s, reported by %s at %s"
msgstr ""
-#: perllib/Page.pm:464
-msgid "your update will not be posted"
+#: perllib/FixMyStreet/Cobrand/Default.pm:769 perllib/FixMyStreet/Cobrand/Default.pm:783
+msgid "%s, within %s ward"
msgstr ""
-#: perllib/Page.pm:465
-msgid "we'll hang on to your update while you're checking your email."
+#: perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm:21 perllib/FixMyStreet/Map/Tilma/Original/1_10k.pm:21
+msgid "&copy; Crown copyright. All rights reserved. Ministry of Justice 100037819&nbsp;2008."
msgstr ""
-#: perllib/Page.pm:467
-msgid "your alert will not be activated"
+#: templates/web/default/email_sent.html:32
+msgid "(Don't worry &mdash; %s)"
msgstr ""
-#: perllib/Page.pm:468
-msgid "we'll hang on to your alert while you're checking your email."
+#: templates/web/default/alert/list.html:53
+msgid "(a default distance which covers roughly 200,000 people)"
msgstr ""
-#: perllib/Page.pm:475
-#, perl-format
+#: templates/web/default/alert/list.html:58
+msgid "(alternatively the RSS feed can be customised, within"
+msgstr ""
+
+#: templates/web/default/around/around_map_list_items.html:10 templates/web/default/around/on_map_list_items.html:7
+msgid "(fixed)"
+msgstr ""
+
+#: templates/web/default/index.html:4 templates/web/default/index.html:8
+msgid "(like graffiti, fly tipping, broken paving slabs, or street lighting)"
+msgstr ""
+
+#: templates/web/default/reports/council.html:90
+msgid "(not sent to council)"
+msgstr ""
+
+#: templates/web/default/report/display.html:82 templates/web/default/report/new/fill_in_details.html:160
+msgid "(optional)"
+msgstr ""
+
+#: templates/web/default/reports/council.html:88
+msgid "(sent to both)"
+msgstr ""
+
+#: templates/web/default/report/display.html:88 templates/web/default/report/new/fill_in_details.html:145
+msgid "(we never show your email address or phone number)"
+msgstr ""
+
+#: perllib/FixMyStreet/App/Controller/Admin.pm:261
+msgid "*unknown*"
+msgstr ""
+
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:475 perllib/FixMyStreet/App/Controller/Report/New.pm:501 perllib/FixMyStreet/DB/Result/Problem.pm:204
+msgid "-- Pick a category --"
+msgstr ""
+
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:461 perllib/FixMyStreet/DB/Result/Problem.pm:210
+msgid "-- Pick a property type --"
+msgstr ""
+
+#: templates/web/default/tokens/confirm_problem.html:14 templates/web/default/tokens/confirm_problem.html:6
+msgid ". You can <a href=\"%s\">view the problem on this site</a>."
+msgstr ""
+
+#: templates/web/default/footer.html:13
+msgid "<a href=\"http://www.mysociety.org/\"><img id=\"logo\" width=\"133\" height=\"26\" src=\"/i/mysociety-dark.png\" alt=\"View mySociety.org\"><span id=\"logoie\"></span></a>"
+msgstr ""
+
+#: templates/web/default/questionnaire/completed.html:21
+msgid "<p style=\"font-size:150%\">Thank you very much for filling in our questionnaire; glad to hear it&rsquo;s been fixed.</p>"
+msgstr ""
+
+#: templates/web/default/questionnaire/completed.html:11
msgid ""
-"<h1>Nearly Done! Now check your email...</h1>\n"
-"<p>The confirmation email <strong>may</strong> take a few minutes to arrive "
-"&mdash; <em>please</em> be patient.</p>\n"
-"<p>If you use web-based email or have 'junk mail' filters, you may wish to "
-"check your bulk/spam mail folders: sometimes, our messages are marked that "
-"way.</p>\n"
-"<p>You must now click the link in the email we've just sent you &mdash;\n"
-"if you do not, %s.</p>\n"
-"<p>(Don't worry &mdash; %s)</p>\n"
-msgstr ""
-
-#: perllib/Page.pm:503
-msgid "today"
+"<p style=\"font-size:150%%\">We&rsquo;re sorry to hear that. We have two suggestions: why not try\n"
+"<a href=\"%s\">writing direct to your councillor(s)</a>\n"
+"or, if it&rsquo;s a problem that could be fixed by local people working together,\n"
+"why not <a href=\"http://www.pledgebank.com/new\">make and publicise a pledge</a>?\n"
+"</p>"
msgstr ""
-#: perllib/Page.pm:527
-msgid "less than a minute"
+#: templates/web/default/questionnaire/index.html:37
+msgid ""
+"<p>Getting empty homes back into use can be difficult, but by now a good council\n"
+"will have made a lot of progress and reported what they have done on the\n"
+"website. Even so properties can remain empty for many months if the owner is\n"
+"unwilling or the property is in very poor repair. If nothing has happened or\n"
+"you are not satisfied with the progress the council is making, now is the right\n"
+"time to say so. We think it&rsquo;s a good idea to contact some other people who\n"
+"may be able to help or put pressure on the council For advice on how to do\n"
+"this and other useful information please go to <a\n"
+"href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://www.emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
msgstr ""
-#: perllib/Page.pm:530
-#, perl-format
-msgid "%d week"
+#: templates/web/default/questionnaire/index.html:28
+msgid ""
+"<p>Getting empty homes back into use can be difficult. You shouldn&rsquo;t expect\n"
+"the property to be back into use yet. But a good council will have started work\n"
+"and should have reported what they have done on the website. If you are not\n"
+"satisfied with progress or information from the council, now is the right time\n"
+"to say. You may also want to try contacting some other people who may be able\n"
+"to help. For advice on how to do this and other useful information please\n"
+"go to <a href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://www.emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
msgstr ""
-#: perllib/Page.pm:530
-#, perl-format
-msgid "%d weeks"
+#: templates/web/default/questionnaire/completed.html:5
+msgid ""
+"<p>Thank you very much for filling in our questionnaire; if you\n"
+"get some more information about the status of your problem, please come back to the\n"
+"site and leave an update.</p>"
msgstr ""
-#: perllib/Page.pm:531
-#, perl-format
-msgid "%d day"
+#: templates/web/default/around/display_location.html:93 templates/web/default/around/display_location.html:95
+msgid "<small>If you cannot see the map, <a href='%s' rel='nofollow'>skip this step</a>.</small>"
msgstr ""
-#: perllib/Page.pm:531
-#, perl-format
-msgid "%d days"
+#: templates/web/default/admin/index.html:14
+msgid "<strong>%d</strong> live problems"
msgstr ""
-#: perllib/Page.pm:532
-#, perl-format
-msgid "%d hour"
+#: templates/web/default/static/about.html:1 templates/web/default/static/about.html:3 templates/web/emptyhomes/header.html:34 templates/web/emptyhomes/static/about.html:1 templates/web/emptyhomes/static/about.html:3
+msgid "About us"
msgstr ""
-#: perllib/Page.pm:532
-#, perl-format
-msgid "%d hours"
+#: templates/web/default/admin/council_contacts.html:50
+msgid "Add new category"
msgstr ""
-#: perllib/Page.pm:533
-#, perl-format
-msgid "%d minute"
+#: templates/web/default/auth/change_password.html:30
+msgid "Again:"
msgstr ""
-#: perllib/Page.pm:533
-#, perl-format
-msgid "%d minutes"
+#: templates/web/default/admin/timeline.html:37
+msgid "Alert %d created for %s, type %s, parameters %s / %s"
msgstr ""
-#: perllib/Page.pm:553
-#, perl-format
-msgid "%s, reported anonymously at %s"
+#: templates/web/default/admin/timeline.html:39
+msgid "Alert %d disabled (created %s)"
msgstr ""
-#: perllib/Page.pm:555
-#, perl-format
-msgid "%s, reported by %s at %s"
+#: templates/web/default/report/display.html:133
+msgid "Alert me to future updates"
msgstr ""
-#: perllib/Page.pm:558 perllib/Page.pm:560 perllib/Page.pm:566
-#: perllib/Page.pm:568 web/index.cgi:408 web/index.cgi:594 web/index.cgi:603
-#: bin/send-reports:186 bin/send-reports:195
-msgid "Other"
+#: templates/web/default/admin/council_list.html:44
+msgid "All confirmed"
msgstr ""
-#: perllib/Page.pm:559
-#, perl-format
-msgid "Reported by %s in the %s category anonymously at %s"
+#: templates/web/default/footer.html:7 templates/web/emptyhomes/header.html:31 templates/web/fiksgatami/footer.html:6
+msgid "All reports"
msgstr ""
-#: perllib/Page.pm:561
-#, perl-format
-msgid "Reported by %s in the %s category by %s at %s"
+#: templates/web/default/report/new/some_councils_text.html:2
+msgid "All the information you provide here will be sent to"
msgstr ""
-#: perllib/Page.pm:563
-#, perl-format
-msgid "Reported by %s anonymously at %s"
+#: templates/web/default/report/new/all_councils_text.html:3 templates/web/default/report/new/all_councils_text.html:5
+msgid "All the information you provide here will be sent to <strong>%s</strong> or a relevant local body such as <strong>TfL</strong>, via the London Report-It system."
msgstr ""
-#: perllib/Page.pm:565
-#, perl-format
-msgid "Reported by %s by %s at %s"
+#: templates/web/default/report/new/all_councils_text.html:10 templates/web/default/report/new/all_councils_text.html:12
+msgid "All the information you provide here will be sent to <strong>%s</strong>."
msgstr ""
-#: perllib/Page.pm:567
-#, perl-format
-msgid "Reported in the %s category anonymously at %s"
+#: templates/web/emptyhomes/report/new/all_councils_text.html:2 templates/web/emptyhomes/report/new/all_councils_text.html:4
+msgid "All the information you provide here will be sent to <strong>%s</strong>. On the site, we will show the subject and details of the problem, plus your name if you give us permission."
msgstr ""
-#: perllib/Page.pm:569
-#, perl-format
-msgid "Reported in the %s category by %s at %s"
+#: templates/web/default/questionnaire/index.html:62
+msgid "An update marked this problem as fixed."
msgstr ""
-#: perllib/Page.pm:571 web/contact.cgi:212
-#, perl-format
-msgid "Reported anonymously at %s"
+#: templates/web/default/admin/list_updates.html:10 templates/web/default/admin/search_reports.html:18
+msgid "Anonymous"
msgstr ""
-#: perllib/Page.pm:573 web/contact.cgi:213
-#, perl-format
-msgid "Reported by %s at %s"
+#: templates/web/default/admin/report_edit.html:17 templates/web/default/admin/update_edit.html:14
+msgid "Anonymous:"
msgstr ""
-#: perllib/Page.pm:578
-msgid "the map was not used so pin location may be inaccurate"
+#: templates/web/fiksgatami/footer.html:15
+msgid "Built by <a href=\"http://www.mysociety.org/\">mySociety</a> and maintained by <a href=\"http://www.nuug.no/\">NUUG</a>, using some <a href=\"http://github.com/mysociety/fixmystreet\">clever</a>&nbsp;<a href=\"https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa\">code</a>."
msgstr ""
-#: perllib/Page.pm:591
-#, perl-format
-msgid "Sent to %s %s later"
+#: templates/web/default/footer.html:16
+msgid "Built by <a href=\"http://www.mysociety.org/\">mySociety</a>, using some <a href=\"http://github.com/mysociety/fixmystreet\">clever</a>&nbsp;<a href=\"https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa\">code</a>."
msgstr ""
-#: perllib/Page.pm:595
-msgid "Not reported to council"
+#: templates/web/default/report/new/fill_in_details.html:141
+msgid "Can we show your name on the site?"
msgstr ""
-#: perllib/Page.pm:646 web-admin/index.cgi:523 web-admin/index.cgi:647
-msgid "Updates"
+#: templates/web/default/report/display.html:87 templates/web/default/report/new/fill_in_details.html:143
+msgid "Can we show your name publicly?"
msgstr ""
-#: perllib/Page.pm:650
-#, perl-format
-msgid "Posted by %s at %s"
+#: templates/web/default/admin/council_contacts.html:19 templates/web/default/admin/search_reports.html:17
+msgid "Category"
msgstr ""
-#: perllib/Page.pm:652
-#, perl-format
-msgid "Posted anonymously at %s"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:478 perllib/FixMyStreet/App/Controller/Report/New.pm:502 templates/web/default/admin/council_contacts.html:56 templates/web/default/admin/council_edit.html:23 templates/web/default/admin/report_edit.html:26
+msgid "Category:"
msgstr ""
-#: perllib/Page.pm:655
-msgid "marked as fixed"
+#: bin/send-reports:170
+msgid "Category: %s"
msgstr ""
-#: perllib/Page.pm:656
-msgid "reopened"
+#: templates/web/default/auth/change_password.html:1 templates/web/default/auth/change_password.html:3 templates/web/default/auth/change_password.html:35
+msgid "Change Password"
msgstr ""
-#: perllib/Page.pm:664 web/index.cgi:1106
-msgid "Offensive? Unsuitable? Tell us"
+#: templates/web/default/around/display_location.html:111 templates/web/default/around/display_location.html:113
+msgid "Closest nearby problems <small>(within&nbsp;%skm)</small>"
msgstr ""
-#: perllib/Page.pm:691
-msgid "That postcode was not recognised, sorry."
+#: templates/web/default/admin/report_edit.html:15
+msgid "Co-ordinates:"
msgstr ""
-#: perllib/Page.pm:697
-msgid ""
-"Sorry, that appears to be a Crown dependency postcode, which we don't cover."
+#: templates/web/default/admin/list_updates.html:11 templates/web/default/admin/search_reports.html:19
+msgid "Cobrand"
msgstr ""
-#: perllib/Page.pm:700 perllib/FixMyStreet/Geocode.pm:145
-msgid ""
-"We do not cover Northern Ireland, I'm afraid, as our licence doesn't include "
-"any maps for the region."
+#: templates/web/default/admin/report_edit.html:36 templates/web/default/admin/update_edit.html:26
+msgid "Cobrand data:"
msgstr ""
-#: perllib/Page.pm:731
-msgid "Please upload a JPEG image only"
+#: templates/web/default/admin/report_edit.html:35 templates/web/default/admin/update_edit.html:25
+msgid "Cobrand:"
msgstr ""
-#: perllib/FixMyStreet/Alert.pm:147
-msgid "This report is currently marked as fixed."
+#: templates/web/default/admin/council_contacts.html:26
+msgid "Confirm"
msgstr ""
-#: perllib/FixMyStreet/Alert.pm:148
-msgid "This report is currently marked as open."
+#: templates/web/default/auth/token.html:1
+msgid "Confirm account"
msgstr ""
-#: perllib/FixMyStreet/Alert.pm:313
-#, perl-format
-msgid "Report on %s"
+#: templates/web/default/questionnaire/creator_fixed.html:1 templates/web/default/tokens/confirm_problem.html:1 templates/web/default/tokens/confirm_problem.html:3 templates/web/default/tokens/confirm_update.html:1 templates/web/default/tokens/confirm_update.html:3 templates/web/emptyhomes/tokens/confirm_problem.html:1 templates/web/emptyhomes/tokens/confirm_problem.html:3
+msgid "Confirmation"
msgstr ""
-#: perllib/FixMyStreet/Geocode.pm:73
-msgid ""
-"Sorry, that location appears to be too general; please be more specific."
+#: templates/web/default/admin/council_contacts.html:21 templates/web/default/admin/council_contacts.html:66 templates/web/default/admin/council_edit.html:28 templates/web/default/admin/council_edit.html:43
+msgid "Confirmed"
msgstr ""
-#: perllib/FixMyStreet/Geocode.pm:83
-msgid "That location does not appear to be in Britain; please try again."
+#: templates/web/default/admin/report_edit.html:31 templates/web/default/admin/search_reports.html:47
+msgid "Confirmed:"
msgstr ""
-#: perllib/FixMyStreet/Geocode.pm:105 perllib/FixMyStreet/Geocode.pm:140
-msgid "Sorry, we could not find that location."
+#: templates/web/default/footer.html:10 templates/web/fiksgatami/footer.html:9
+msgid "Contact"
msgstr ""
-#: perllib/FixMyStreet/Geocode.pm:138
-msgid "Sorry, we could not parse that location. Please try again."
+#: templates/web/default/contact/index.html:1 templates/web/default/contact/index.html:2 templates/web/default/contact/submit.html:1
+msgid "Contact Us"
msgstr ""
-#: perllib/FixMyStreet/Geocode.pm:158
-msgid ""
-"We found more than one match for that location. We show up to ten matches, "
-"please try a different search if yours is not here."
+#: templates/web/default/contact/index.html:6 templates/web/default/contact/submit.html:3
+msgid "Contact the team"
msgstr ""
-#: perllib/FixMyStreet/Geocode.pm:173
-msgid "More than one match"
+#: templates/web/default/admin/search_reports.html:16
+msgid "Council"
msgstr ""
-#: perllib/FixMyStreet/Map/Bing.pm:41
-#: perllib/FixMyStreet/Map/OSM/StreetView.pm:42
-#: perllib/FixMyStreet/Map/Google.pm:41
-#: perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm:51
-#: perllib/FixMyStreet/Map/Tilma/Original/StreetView.pm:21
-msgid ""
-"Map contains Ordnance Survey data &copy; Crown copyright and database right "
-"2010."
+#: perllib/FixMyStreet/App/Controller/Admin.pm:598 templates/web/default/admin/council_list.html:1
+msgid "Council contacts"
msgstr ""
-#: perllib/FixMyStreet/Map/OSM.pm:76
-msgid ""
-"Map &copy; <a id=\"osm_link\" href=\"http://www.openstreetmap.org/"
-"\">OpenStreetMap</a> and contributors, <a href=\"http://creativecommons.org/"
-"licenses/by-sa/2.0/\">CC-BY-SA</a>"
+#: templates/web/default/admin/council_contacts.html:1 templates/web/default/admin/council_edit.html:1
+msgid "Council contacts for %s"
msgstr ""
-#: perllib/FixMyStreet/Map/OSM.pm:115
-#: perllib/FixMyStreet/Map/Tilma/Original.pm:155
-msgid "Problem"
+#: templates/web/default/admin/council_list.html:36
+msgid "Councils"
msgstr ""
-#: perllib/FixMyStreet/Map/BingOL.pm:44
-msgid ""
-"Map contains Ordnance Survey data &copy; Crown copyright and database right "
-"2010. Microsoft"
+#: templates/web/default/email_sent.html:1
+msgid "Create a report"
msgstr ""
-#: perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm:51
-#: perllib/FixMyStreet/Map/Tilma/Original/1_10k.pm:21
-msgid ""
-"&copy; Crown copyright. All rights reserved. Ministry of Justice "
-"100037819&nbsp;2008."
+#: templates/web/default/admin/council_contacts.html:80
+msgid "Create category"
msgstr ""
-#: perllib/FixMyStreet/Map/Tilma/Original.pm:86
-msgid "Unable to fetch the map tiles from the tile server."
+#: templates/web/default/admin/list_updates.html:9 templates/web/default/admin/search_reports.html:20
+msgid "Created"
msgstr ""
-#: perllib/Problems.pm:148
-#, perl-format
-msgid "<big>%s</big> report in past week"
-msgid_plural "<big>%s</big> reports in past week"
-msgstr[0] ""
-msgstr[1] ""
+#: templates/web/default/admin/report_edit.html:30 templates/web/default/admin/update_edit.html:27
+msgid "Created:"
+msgstr ""
-#: perllib/Problems.pm:153
-#, perl-format
-msgid "<big>%s</big> report recently"
-msgid_plural "<big>%s</big> reports recently"
-msgstr[0] ""
-msgstr[1] ""
+#: templates/web/default/admin/council_list.html:40
+msgid "Currently has 1+ deleted"
+msgstr ""
-#: perllib/Problems.pm:159
-msgid "FixMyStreet updates"
+#: templates/web/default/admin/council_contacts.html:22 templates/web/default/admin/council_contacts.html:69 templates/web/default/admin/council_edit.html:29 templates/web/default/admin/council_edit.html:44
+msgid "Deleted"
msgstr ""
-#: perllib/Problems.pm:162 perllib/Problems.pm:166
-#, perl-format
-msgid "<big>%s</big> fixed in past month"
-msgid_plural "<big>%s</big> fixed in past month"
-msgstr[0] ""
-msgstr[1] ""
+#: templates/web/default/admin/report_edit.html:14 templates/web/default/report/new/fill_in_details.html:100
+msgid "Details:"
+msgstr ""
-#: perllib/Problems.pm:167
-#, perl-format
-msgid "<big>%s</big> update on reports"
-msgid_plural "<big>%s</big> updates on reports"
-msgstr[0] ""
-msgstr[1] ""
+#: templates/web/default/admin/council_list.html:23
+msgid "Diligency prize league table"
+msgstr ""
-#: web/about.cgi:24
-msgid ""
-"<h2>The Empty Homes Agency</h2>\n"
-"<p>The Empty Homes agency is an independent campaigning charity. We are not\n"
-"part of government, and have no formal links with local councils although "
-"we\n"
-"work in cooperation with both. We exist to highlight the waste of empty\n"
-"property and work with others to devise and promote sustainable solutions "
-"to\n"
-"bring empty property back into use. We are based in London but work across\n"
-"England. We also work in partnership with other charities across the UK.</"
-"p>\n"
-msgstr ""
-
-#: web/about.cgi:34
-msgid ""
-"<h2>Shelter Cymru</h2>\n"
-"Shelter Cymru is Wales&rsquo; people and homes charity and wants everyone in "
-"Wales to\n"
-"have a decent home. We believe a home is a fundamental right and essential "
-"to\n"
-"the health and well-being of people and communities. We work for people in\n"
-"housing need. We have offices all over Wales and prevent people from losing\n"
-"their homes by offering free, confidential and independent advice. When\n"
-"necessary we constructively challenge on behalf of people to ensure they "
-"are\n"
-"properly assisted and to improve practice and learning. We believe that\n"
-"bringing empty homes back into use can make a significant contribution to "
-"the\n"
-"supply of affordable homes in Wales.\n"
-"<a href=\"http://www.sheltercymru.org.uk/shelter/advice/pdetail.asp?cat=20"
-"\">Further information about our work on\n"
-"empty homes</a>.\n"
-msgstr ""
-
-#: web/ajax.cgi:55 web/ajax.cgi:76 web/index.cgi:935 web/index.cgi:950
-msgid "(fixed)"
+#: templates/web/default/questionnaire/index.html:72
+msgid "Don&rsquo;t know"
msgstr ""
-#: web/alert.cgi:33 web/confirm.cgi:49
-msgid "Confirmation"
+#: templates/web/default/admin/list_updates.html:31 templates/web/default/admin/search_reports.html:52
+msgid "Edit"
msgstr ""
-#: web/alert.cgi:43
-#, perl-format
-msgid ""
-"Thank you for trying to confirm your alert. We seem to have an error "
-"ourselves\n"
-"though, so <a href=\"%s\">please let us know what went on</a> and we'll look "
-"into it.\n"
+#: templates/web/default/admin/report_edit.html:1
+msgid "Editing problem %d"
msgstr ""
-#: web/alert.cgi:59 web/alert.cgi:62 web/alert.cgi:65 web/alert.cgi:274
-#: web/alert.cgi:380
-msgid "Local RSS feeds and email alerts"
+#: templates/web/default/admin/update_edit.html:1
+msgid "Editing update %d"
msgstr ""
-#: web/alert.cgi:121
-msgid ""
-"That location does not appear to be covered by a council, perhaps it is "
-"offshore - please try somewhere more specific."
+#: templates/web/default/admin/council_edit.html:45
+msgid "Editor"
msgstr ""
-#: web/alert.cgi:140 web/alert.cgi:183 web/alert.cgi:200
-#, perl-format
-msgid "Problems within %s"
+#: templates/web/default/admin/council_contacts.html:20 templates/web/default/admin/council_edit.html:42 templates/web/default/admin/list_updates.html:8 templates/web/default/admin/search_reports.html:15 templates/web/default/report/display.html:95
+msgid "Email"
msgstr ""
-#: web/alert.cgi:152 web/alert.cgi:233
-msgid "Problems within the boundary of:"
+#: templates/web/default/around/display_location.html:77
+msgid "Email me new local problems"
msgstr ""
-#: web/alert.cgi:158 web/alert.cgi:245
-msgid "Or problems reported to:"
+#: templates/web/default/report/display.html:42
+msgid "Email me updates"
msgstr ""
-#: web/alert.cgi:160 web/alert.cgi:247
-msgid ""
-"FixMyStreet sends different categories of problem\n"
-"to the appropriate council, so problems within the boundary of a particular "
-"council\n"
-"might not match the problems sent to that council. For example, a graffiti "
-"report\n"
-"will be sent to the district council, so will appear in both of the "
-"district\n"
-"council&rsquo;s alerts, but will only appear in the \"Within the boundary\" "
-"alert\n"
-"for the county council."
+#: templates/web/default/auth/general.html:47
+msgid "Email the details I need to the address I entered above"
msgstr ""
-#: web/alert.cgi:185
-#, perl-format
-msgid "Problems within %s ward"
+#: templates/web/default/admin/council_contacts.html:61 templates/web/default/admin/council_edit.html:26 templates/web/default/admin/report_edit.html:28 templates/web/default/admin/update_edit.html:24 templates/web/default/alert/updates.html:13 templates/web/default/auth/general.html:29 templates/web/default/report/display.html:46 templates/web/default/report/new/fill_in_details.html:153
+msgid "Email:"
msgstr ""
-#: web/alert.cgi:269
-msgid "Photos of recent nearby reports"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:463
+msgid "Empty flat or maisonette"
msgstr ""
-#: web/alert.cgi:272
-#, perl-format
-msgid "Local RSS feeds and email alerts for &lsquo;%s&rsquo;"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:462
+msgid "Empty house or bungalow"
msgstr ""
-#: web/alert.cgi:286
-#, perl-format
-msgid "Here are the types of local problem alerts for &lsquo;%s&rsquo;."
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:465
+msgid "Empty office or other commercial"
msgstr ""
-#: web/alert.cgi:287
-msgid ""
-"Select which type of alert you&rsquo;d like and click the button for an RSS\n"
-"feed, or enter your email address to subscribe to an email alert."
+#: templates/web/emptyhomes/report/new/form_heading.html:1
+msgid "Empty property details form"
msgstr ""
-#: web/alert.cgi:290
-msgid "The simplest alert is our geographic one:"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:466
+msgid "Empty pub or bar"
msgstr ""
-#: web/alert.cgi:291
-#, perl-format
-msgid "Problems within %skm of this location"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:467
+msgid "Empty public building - school, hospital, etc."
msgstr ""
-#: web/alert.cgi:305
-msgid "(a default distance which covers roughly 200,000 people)"
+#: templates/web/default/around/around_index.html:10 templates/web/default/around/around_index.html:13 templates/web/default/index.html:40 templates/web/default/index.html:43
+msgid "Enter a nearby GB postcode, or street name and area"
msgstr ""
-#: web/alert.cgi:308
-msgid "RSS feed of nearby problems"
+#: perllib/FixMyStreet/Cobrand/FiksGataMi.pm:20
+msgid "Enter a nearby postcode, or street name and area"
msgstr ""
-#: web/alert.cgi:308 web/alert.cgi:367 web/index.cgi:1005 web/index.cgi:1117
-#: web/reports.cgi:254
-msgid "RSS feed"
+#: templates/web/default/index.html:61
+msgid "Enter details of the problem"
msgstr ""
-#: web/alert.cgi:309
-msgid "(alternatively the RSS feed can be customised, within"
+#: templates/web/default/auth/token.html:5 templates/web/default/questionnaire/error.html:1 templates/web/default/questionnaire/error.html:3 templates/web/default/tokens/abuse.html:1 templates/web/default/tokens/abuse.html:3 templates/web/default/tokens/error.html:1 templates/web/default/tokens/error.html:3
+msgid "Error"
msgstr ""
-#: web/alert.cgi:319
-msgid ""
-"Or you can subscribe to an alert based upon what ward or council you&rsquo;"
-"re in:"
+#: templates/web/default/admin/council_contacts.html:9 templates/web/default/admin/council_edit.html:18
+msgid "Example postcode %s"
msgstr ""
-#: web/alert.cgi:323
-msgid "Give me an RSS feed"
+#: templates/web/default/contact/submit.html:15
+msgid "Failed to send message. Please try again, or <a href=\"mailto:%s\">email us</a>."
msgstr ""
-#: web/alert.cgi:324
-msgid "or"
+#: templates/web/default/questionnaire/index.html:81
+msgid "First time"
msgstr ""
-#: web/alert.cgi:325
-msgid "Your email:"
+#: templates/web/default/header.html:16 templates/web/fiksgatami/header.html:14
+msgid "Fix<span id=\"my\">My</span>Street"
msgstr ""
-#: web/alert.cgi:326
-msgid "Subscribe me to an email alert"
+#: templates/web/default/admin/header.html:12
+msgid "FixMyStreet admin:"
msgstr ""
-#: web/alert.cgi:367
-#, perl-format
-msgid "RSS feed of %s"
+#: templates/web/default/admin/header.html:3
+msgid "FixMyStreet administration"
msgstr ""
-#: web/alert.cgi:381
+#: templates/web/default/alert/index.html:6
msgid ""
-"FixMyStreet has a variety of RSS feeds and email alerts for local problems, "
-"including\n"
-"alerts for all problems within a particular ward or council, or all "
-"problems\n"
+"FixMyStreet has a variety of RSS feeds and email alerts for local problems, including\n"
+"alerts for all problems within a particular ward or council, or all problems\n"
"within a certain distance of a particular location."
msgstr ""
-#: web/alert.cgi:384
+#: templates/web/default/alert/list.html:101
msgid ""
-"To find out what local alerts we have for you, please enter your GB\n"
-"postcode or street name and area:"
+"FixMyStreet sends different categories of problem\n"
+"to the appropriate council, so problems within the boundary of a particular council\n"
+"might not match the problems sent to that council. For example, a graffiti report\n"
+"will be sent to the district council, so will appear in both of the district\n"
+"council&rsquo;s alerts, but will only appear in the \"Within the boundary\" alert\n"
+"for the county council."
msgstr ""
-#: web/alert.cgi:389 web/index.cgi:167
-msgid "Go"
+#: templates/web/default/front_stats.html:7
+msgid "FixMyStreet updates"
msgstr ""
-#: web/alert.cgi:415
-msgid "Some photos of recent reports"
+#: templates/web/default/admin/report_edit.html:22
+msgid "Fixed"
msgstr ""
-#: web/alert.cgi:423
-msgid "Please select the feed you want"
+#: templates/web/default/admin/search_reports.html:48
+msgid "Fixed:"
msgstr ""
-#: web/alert.cgi:446
-msgid "Illegal feed selection"
+#: templates/web/default/admin/report_edit.html:16
+msgid "For council(s):"
msgstr ""
-#: web/alert.cgi:460
-msgid "Receive email when updates are left on this problem."
+#: templates/web/default/faq/faq-en-gb.html:1 templates/web/emptyhomes/faq/faq-cy.html:1 templates/web/emptyhomes/faq/faq-en-gb.html:1 templates/web/fiksgatami/faq/faq-nb.html:1
+msgid "Frequently Asked Questions"
msgstr ""
-#: web/alert.cgi:461 web/alert.cgi:488 web/index.cgi:769 web/index.cgi:1111
-#: web-admin/index.cgi:631 web-admin/index.cgi:751
-msgid "Email:"
+#: templates/web/emptyhomes/static/about.html:34
+msgid "Further information about our work on empty homes."
msgstr ""
-#: web/alert.cgi:462 web/alert.cgi:489 web/index.cgi:1112
-msgid "Subscribe"
+#: templates/web/default/index.html:22
+msgid "Get FixMyStreet on your iPhone"
msgstr ""
-#: web/alert.cgi:487
-msgid "Receive alerts on new local problems"
+#: templates/web/default/alert/list.html:115
+msgid "Give me an RSS feed"
msgstr ""
-#: web/alert.cgi:515
-msgid "You have successfully subscribed to that alert."
+#: templates/web/default/alert/index.html:24 templates/web/default/around/around_index.html:17 templates/web/default/index.html:49
+msgid "Go"
msgstr ""
-#: web/alert.cgi:522
-msgid "We could not validate that alert."
+#: templates/web/default/admin/report_edit.html:37
+msgid "Going to send questionnaire?"
msgstr ""
-#: web/alert.cgi:544
-msgid "You have successfully confirmed your alert."
+#: templates/web/default/admin/index.html:23
+msgid "Graph of problem creation by status over time"
msgstr ""
-#: web/alert.cgi:551
-msgid "You have successfully deleted your alert."
+#: templates/web/default/reports/index.html:5 templates/web/emptyhomes/reports/index.html:5
+msgid "Greyed-out lines are councils that no longer exist."
msgstr ""
-#: web/alert.cgi:571
-msgid "Please enter a valid email address"
+#: templates/web/default/questionnaire/index.html:63
+msgid "Has this problem been fixed?"
msgstr ""
-#: web/alert.cgi:572
-msgid "Please select the type of alert you want"
+#: templates/web/default/questionnaire/index.html:76
+msgid "Have you ever reported a problem to a council before, or is this your first time?"
msgstr ""
-#: web/confirm.cgi:39
-#, perl-format
-msgid ""
-"Thank you for trying to confirm your update or problem. We seem to have an\n"
-"error ourselves though, so <a href=\"%s\">please let us know what went on</"
-"a>\n"
-"and we'll look into it.\n"
+#: templates/web/default/footer.html:9 templates/web/emptyhomes/header.html:33 templates/web/fiksgatami/footer.html:8
+msgid "Help"
msgstr ""
-#: web/confirm.cgi:101
-#, perl-format
-msgid ""
-"You have successfully confirmed your update and you can now <a href=\"%s"
-"\">view it on the site</a>."
+#: templates/web/default/alert/list.html:39
+msgid "Here are the types of local problem alerts for &lsquo;%s&rsquo;."
msgstr ""
-#: web/confirm.cgi:131
-msgid "Sorry, there has been an error confirming your problem."
+#: templates/web/default/admin/report_edit.html:22 templates/web/default/admin/update_edit.html:19
+msgid "Hidden"
msgstr ""
-#: web/confirm.cgi:139
-msgid ""
-"Thank you for reporting an empty property on\n"
-"ReportEmptyHomes.com. We have emailed the lead officer for empty homes in "
-"the council\n"
-"responsible with details, and asked them to do whatever they can to get the\n"
-"empty property back into use as soon as possible."
+#: templates/web/default/around/display_location.html:54
+msgid "Hide pins"
msgstr ""
-#: web/confirm.cgi:143
-msgid ""
-"It is worth noting however that the process can sometimes be slow,\n"
-"especially if the property is in very poor repair or the owner is unwilling "
-"to\n"
-"act. In most cases it can take six months or more before you can expect to "
-"see\n"
-"anything change and sometimes there may be considerable barries to a "
-"property\n"
-"being brought back into use. This doesn&rsquo;t mean the council isn&rsquo;"
-"t\n"
-"doing anything. We encourage councils to update the website so you can\n"
-"see what is happening. It may be a long process, but you reporting your\n"
-"concerns about this property to the council is a valuable first step."
-msgstr ""
-
-#: web/confirm.cgi:151
-msgid ""
-"We may contact you periodically to ask if anything has changed\n"
-"with the property you reported."
+#: templates/web/default/around/display_location.html:59
+msgid "Hide stale reports"
msgstr ""
-#: web/confirm.cgi:153
-msgid ""
-"Thank you for using ReportEmptyHomes.com. Your action is already helping\n"
-"to resolve the UK&rsquo;s empty homes crisis."
+#: templates/web/default/admin/council_edit.html:38
+msgid "History"
msgstr ""
-#: web/confirm.cgi:155 web/confirm.cgi:161
-msgid "View your report"
+#: templates/web/default/index.html:56
+msgid "How to report a problem"
msgstr ""
-#: web/confirm.cgi:157
-msgid ""
-"Thank you for reporting this empty property on ReportEmptyHomes.com.\n"
-"At present the report cannot be sent through to the council for this area. "
-"We\n"
-"are working with councils to link them into the system so that as many "
-"areas\n"
-"as possible will be covered."
+#: perllib/FixMyStreet/App/Controller/Admin.pm:516
+msgid "I am afraid you cannot confirm unconfirmed reports."
msgstr ""
-#: web/confirm.cgi:166
-msgid "You have successfully confirmed your problem"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:93
+msgid "I'm afraid we couldn't locate your problem in the database.\n"
msgstr ""
-#: web/confirm.cgi:167
-msgid " and <strong>we will now send it to the council</strong>"
+#: perllib/FixMyStreet/App/Controller/Tokens.pm:161
+msgid "I'm afraid we couldn't validate that token. If you've copied the URL from an email, please check that you copied it exactly.\n"
msgstr ""
-#: web/confirm.cgi:168
-#, perl-format
-msgid ". You can <a href=\"%s\">view the problem on this site</a>."
+#: templates/web/default/admin/list_updates.html:5 templates/web/default/admin/search_reports.html:12
+msgid "ID"
+msgstr ""
+
+#: templates/web/default/report/new/no_councils_text.html:11 templates/web/default/report/new/no_councils_text.html:3
+msgid "If you submit a problem here the subject and details of the problem will be public, but the problem will <strong>not</strong> be reported to the council."
+msgstr ""
+
+#: templates/web/emptyhomes/report/new/no_councils_text.html:9
+msgid "If you submit a report here it will be left on the site, but not reported to the council &ndash; please still leave your report, so that we can show to the council the activity in their area."
+msgstr ""
+
+#: templates/web/default/email_sent.html:28
+msgid "If you use web-based email or have 'junk mail' filters, you may wish to check your bulk/spam mail folders: sometimes, our messages are marked that way."
msgstr ""
-#: web/confirm.cgi:192
+#: templates/web/default/questionnaire/index.html:85
msgid ""
-"Thanks, glad to hear it's been fixed! Could we just ask if you have ever "
-"reported a problem to a council before?"
+"If you wish to leave a public update on the problem, please enter it here\n"
+"(please note it will not be sent to the council). For example, what was\n"
+"your experience of getting the problem fixed?"
msgstr ""
-#: web/confirm.cgi:193 web/questionnaire.cgi:234 web/questionnaire.cgi:323
-#: web-admin/index.cgi:341 web-admin/index.cgi:342 web-admin/index.cgi:504
-#: web-admin/index.cgi:591 web-admin/index.cgi:609
-msgid "Yes"
+#: perllib/FixMyStreet/App/Controller/Contact.pm:109
+msgid "Illegal ID"
msgstr ""
-#: web/confirm.cgi:194 web/questionnaire.cgi:235 web/questionnaire.cgi:324
-#: web-admin/index.cgi:341 web-admin/index.cgi:342 web-admin/index.cgi:504
-#: web-admin/index.cgi:591 web-admin/index.cgi:609
-msgid "No"
+#: perllib/FixMyStreet/App/Controller/Alert.pm:105
+msgid "Illegal feed selection"
msgstr ""
-#: web/confirm.cgi:195 web/index.cgi:821
-msgid "Submit"
+#: templates/web/default/around/display_location.html:61
+msgid "Include stale reports"
msgstr ""
-#: web/confirm.cgi:240
-#, perl-format
-msgid ""
-"Thank you &mdash; you can <a href=\"%s\">view your updated problem</a> on "
-"the site."
+#: templates/web/emptyhomes/tokens/confirm_problem.html:12 templates/web/emptyhomes/tokens/confirm_problem.html:14
+msgid "It is worth noting however that the process can sometimes be slow, especially if the property is in very poor repair or the owner is unwilling to act. In most cases it can take six months or more before you can expect to see anything change and sometimes there may be considerable barries to a property being brought back into use. This doesn&rsquo;t mean the council isn&rsquo;t doing anything. We encourage councils to update the website so you can see what is happening. It may be a long process, but you reporting your concerns about this property to the council is a valuable first step."
msgstr ""
-#: web/contact.cgi:22
-msgid "Contact Us"
+#: templates/web/default/admin/council_contacts.html:23
+msgid "Last editor"
msgstr ""
-#: web/contact.cgi:41
-msgid "Please give your name"
+#: templates/web/default/admin/report_edit.html:33
+msgid "Last update:"
msgstr ""
-#: web/contact.cgi:43
-msgid "Please give your email"
+#: templates/web/default/admin/search_reports.html:49
+msgid "Last&nbsp;update:"
msgstr ""
-#: web/contact.cgi:45
-msgid "Please give a valid email address"
+#: templates/web/default/admin/council_contacts.html:11
+msgid "List all reported problems"
msgstr ""
-#: web/contact.cgi:47
-msgid "Please give a subject"
+#: templates/web/default/alert/choose.html:1 templates/web/default/alert/choose.html:3 templates/web/default/alert/index.html:1 templates/web/default/alert/index.html:3 templates/web/default/alert/list.html:1 templates/web/default/alert/list.html:5 templates/web/default/alert/updates.html:1 templates/web/default/tokens/confirm_alert.html:1 templates/web/default/tokens/confirm_alert.html:3
+msgid "Local RSS feeds and email alerts"
msgstr ""
-#: web/contact.cgi:48
-msgid "Please write a message"
+#: templates/web/default/alert/list.html:1 templates/web/default/alert/list.html:12 templates/web/default/alert/list.html:14 templates/web/default/alert/list.html:3
+msgid "Local RSS feeds and email alerts for ‘%s’"
msgstr ""
-#: web/contact.cgi:49
-msgid "Illegal ID"
+#: templates/web/default/footer.html:8 templates/web/emptyhomes/header.html:32 templates/web/fiksgatami/footer.html:7
+msgid "Local alerts"
msgstr ""
-#: web/contact.cgi:86
-msgid "Thanks for your feedback. We'll get back to you as soon as we can!"
+#: templates/web/default/index.html:60
+msgid "Locate the problem on a map of the area"
msgstr ""
-#: web/contact.cgi:134
-msgid ""
-"We&rsquo;d love to hear what you think about this\n"
-"website. Just fill in the form. Please don&rsquo;t contact us about "
-"individual empty\n"
-"homes; use the box accessed from <a href=\"/\">the front page</a>."
+#: templates/web/default/auth/general.html:43
+msgid "Log me in"
msgstr ""
-#: web/contact.cgi:141
-msgid ""
-"Please do <strong>not</strong> report problems through this form; messages "
-"go to\n"
-"the team behind FixMyStreet, not a council. To report a problem,\n"
-"please <a href=\"/\">go to the front page</a> and follow the instructions."
+#: templates/web/default/auth/general.html:1 templates/web/default/auth/general.html:3
+msgid "Login or create an account"
msgstr ""
-#: web/contact.cgi:144
-#, perl-format
-msgid ""
-"We'd love to hear what you think about this site. Just fill in the form, or "
-"send an email to <a href='mailto:%s'>%s</a>:"
+#: templates/web/default/auth/logout.html:1
+msgid "Logout"
msgstr ""
-#: web/contact.cgi:153 web/index.cgi:463
-msgid "There were problems with your report. Please see below."
+#: perllib/FixMyStreet/Map/OSM.pm:43
+msgid "Map &copy; <a id=\"osm_link\" href=\"http://www.openstreetmap.org/\">OpenStreetMap</a> and contributors, <a href=\"http://creativecommons.org/licenses/by-sa/2.0/\">CC-BY-SA</a>"
msgstr ""
-#: web/contact.cgi:158
-msgid "Contact the team"
+#: perllib/FixMyStreet/Map/OSM/StreetView.pm:27 perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm:21 perllib/FixMyStreet/Map/Tilma/Original/StreetView.pm:21
+msgid "Map contains Ordnance Survey data &copy; Crown copyright and database right 2010."
msgstr ""
-#: web/contact.cgi:196
-msgid ""
-"You are reporting the following update for being abusive, containing "
-"personal information, or similar:"
+#: perllib/FixMyStreet/Map/FMS.pm:27
+msgid "Map contains Ordnance Survey data &copy; Crown copyright and database right 2010. Microsoft"
msgstr ""
-#: web/contact.cgi:207
-msgid ""
-"You are reporting the following problem report for being abusive, containing "
-"personal information, or similar:"
+#: templates/web/default/contact/index.html:90
+msgid "Message:"
msgstr ""
-#: web/contact.cgi:234
-msgid "Your name:"
+#: templates/web/default/report/display.html:38
+msgid "More problems nearby"
msgstr ""
-#: web/contact.cgi:235
-msgid "Your&nbsp;email:"
+#: templates/web/default/my/my.html:1
+msgid "My Reports"
msgstr ""
-#: web/contact.cgi:236 web/index.cgi:765 web-admin/index.cgi:623
-msgid "Subject:"
+#: templates/web/default/admin/list_updates.html:7 templates/web/default/admin/search_reports.html:14 templates/web/default/reports/index.html:10 templates/web/emptyhomes/reports/index.html:10 templates/web/fiksgatami/reports/index.html:9
+msgid "Name"
msgstr ""
-#: web/contact.cgi:237
-msgid "Message:"
+#: templates/web/default/admin/report_edit.html:27 templates/web/default/admin/update_edit.html:23 templates/web/default/report/display.html:81 templates/web/default/report/new/fill_in_details.html:130
+msgid "Name:"
msgstr ""
-#: web/contact.cgi:238 web/index.cgi:1127
-msgid "Post"
+#: templates/web/default/footer.html:4 templates/web/fiksgatami/footer.html:3
+msgid "Navigation"
msgstr ""
-#: web/faq.cgi:18
-msgid "Frequently Asked Questions"
+#: templates/web/default/email_sent.html:24
+msgid "Nearly Done! Now check your email..."
msgstr ""
-#: web/flickr.cgi:45
-msgid ""
-"Thank you for trying to register for your Flickr photos. We seem to have a\n"
-"problem ourselves though, so <a href=\"/contact\">please let us know what "
-"went on</a>\n"
-"and we'll look into it.\n"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:291
+msgid "New category contact added"
msgstr ""
-#: web/fun.cgi:17 web/fun.cgi:25
-msgid "Weird and Wonderful reports"
+#: db/alert_types.pl:18 db/alert_types.pl:22
+msgid "New local problems on FixMyStreet"
msgstr ""
-#: web/index.cgi:82
-msgid "Submitting your report"
+#: db/alert_types_eha.pl:12
+msgid "New local reports on reportemptyhomes.com"
msgstr ""
-#: web/index.cgi:85
-msgid "Submitting your update"
+#: templates/web/default/reports/council.html:43 templates/web/default/reports/council.html:44 templates/web/default/reports/index.html:11 templates/web/emptyhomes/reports/index.html:11 templates/web/fiksgatami/reports/index.html:10
+msgid "New problems"
msgstr ""
-#: web/index.cgi:89 web/index.cgi:669
-msgid "Reporting a problem"
+#: db/alert_types.pl:38
+msgid "New problems for {{COUNCIL}} within {{WARD}} ward on FixMyStreet"
msgstr ""
-#: web/index.cgi:92
-msgid "Viewing a problem"
+#: db/alert_types.pl:26 db/alert_types.pl:30
+msgid "New problems near {{POSTCODE}} on FixMyStreet"
msgstr ""
-#: web/index.cgi:95
-msgid "Viewing a location"
+#: db/alert_types.pl:10
+msgid "New problems on FixMyStreet"
msgstr ""
-#: web/index.cgi:120
-msgid "Enter a nearby GB postcode, or street name and area"
+#: db/alert_types.pl:34
+msgid "New problems to {{COUNCIL}} on FixMyStreet"
msgstr ""
-#: web/index.cgi:134
-msgid "Report, view, or discuss local problems"
+#: db/alert_types.pl:42
+msgid "New problems within {{NAME}}'s boundary on FixMyStreet"
msgstr ""
-#: web/index.cgi:135
-msgid "(like graffiti, fly tipping, broken paving slabs, or street lighting)"
+#: db/alert_types_eha.pl:23
+msgid "New reports for {{COUNCIL}} within {{WARD}} ward on reportemptyhomes.com"
msgstr ""
-#: web/index.cgi:159
-msgid ""
-"Thanks for uploading your photo. We now need to locate your problem, so "
-"please enter a nearby street name or postcode in the box below&nbsp;:"
+#: db/alert_types_eha.pl:5
+msgid "New reports on reportemptyhomes.com"
msgstr ""
-#: web/index.cgi:177
-msgid "How to report a problem"
+#: db/alert_types_eha.pl:16
+msgid "New reports on reportemptyhomes.com near {{POSTCODE}}"
msgstr ""
-#: web/index.cgi:180
-msgid "Locate the problem on a map of the area"
+#: db/alert_types_eha.pl:19
+msgid "New reports to {{COUNCIL}} on reportemptyhomes.com"
msgstr ""
-#: web/index.cgi:181
-msgid "Enter details of the problem"
+#: db/alert_types_eha.pl:27
+msgid "New reports within {{NAME}}'s boundary on reportemptyhomes.com"
msgstr ""
-#: web/index.cgi:182
-msgid "We send it to the council on your behalf"
+#: templates/web/default/index.html:23
+msgid "New!"
msgstr ""
-#: web/index.cgi:197
-msgid "Photos of recent reports"
+#: templates/web/default/admin/council_contacts.html:32 templates/web/default/admin/council_contacts.html:33 templates/web/default/admin/council_edit.html:4 templates/web/default/admin/list_updates.html:28 templates/web/default/admin/report_edit.html:19 templates/web/default/admin/report_edit.html:37 templates/web/default/admin/search_reports.html:43 templates/web/default/admin/update_edit.html:16 templates/web/default/questionnaire/creator_fixed.html:16 templates/web/default/questionnaire/index.html:109 templates/web/default/questionnaire/index.html:70
+msgid "No"
msgstr ""
-#: web/index.cgi:199
-msgid "Recently reported problems"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:187
+msgid "No council selected"
msgstr ""
-#: web/index.cgi:232
-msgid "Please enter a message"
+#: templates/web/default/admin/council_list.html:32
+msgid "No edits have yet been made."
msgstr ""
-#: web/index.cgi:235 web/index.cgi:319
-msgid "Please enter your email"
+#: templates/web/default/admin/council_list.html:38
+msgid "No info at all"
msgstr ""
-#: web/index.cgi:237 web/index.cgi:321
-msgid "Please enter a valid email"
+#: templates/web/default/around/around_map_list_items.html:15
+msgid "No problems found."
msgstr ""
-#: web/index.cgi:246 web/index.cgi:394
-#, perl-format
-msgid ""
-"That image doesn't appear to have uploaded correctly (%s), please try again."
+#: templates/web/default/around/on_map_list_items.html:12
+msgid "No problems have been reported yet."
msgstr ""
-#: web/index.cgi:268 web-admin/index.cgi:478
-msgid "Anonymous"
+#: templates/web/default/admin/council_list.html:5 templates/web/default/admin/report_edit.html:16
+msgid "None"
msgstr ""
-#: web/index.cgi:310
-msgid "No council selected"
+#: templates/web/default/admin/questionnaire.html:6
+msgid "Not reported before"
msgstr ""
-#: web/index.cgi:311
-msgid "Please enter a subject"
+#: templates/web/default/report/_main.html:9
+msgid "Not reported to council"
msgstr ""
-#: web/index.cgi:312
-msgid "Please enter some details"
+#: templates/web/default/admin/council_contacts.html:24 templates/web/default/admin/council_edit.html:46
+msgid "Note"
msgstr ""
-#: web/index.cgi:314
-msgid "Please enter your name"
+#: templates/web/default/admin/council_contacts.html:73 templates/web/default/admin/council_edit.html:31
+msgid "Note:"
msgstr ""
-#: web/index.cgi:316
-msgid ""
-"Please enter your full name, councils need this information - if you do not "
-"wish your name to be shown on the site, untick the box"
+#: templates/web/default/report/display.html:33 templates/web/default/report/updates.html:19
+msgid "Offensive? Unsuitable? Tell us"
msgstr ""
-#: web/index.cgi:323 web/index.cgi:587 web/index.cgi:603
-msgid "-- Pick a category --"
+#: templates/web/default/reports/council.html:72 templates/web/default/reports/council.html:73
+msgid "Old fixed"
msgstr ""
-#: web/index.cgi:324 web/index.cgi:363 web/index.cgi:370
-msgid "Please choose a category"
+#: templates/web/default/reports/council.html:60 templates/web/default/reports/council.html:61
+msgid "Old problems, state unknown"
msgstr ""
-#: web/index.cgi:326 web/index.cgi:580
-msgid "-- Pick a property type --"
+#: templates/web/default/reports/index.html:13 templates/web/fiksgatami/reports/index.html:12
+msgid "Old problems,<br>state unknown"
msgstr ""
-#: web/index.cgi:327
-msgid "Please choose a property type"
+#: templates/web/default/reports/index.html:15 templates/web/emptyhomes/reports/index.html:14 templates/web/fiksgatami/reports/index.html:14
+msgid "Older fixed"
msgstr ""
-#: web/index.cgi:350
-msgid "That location is not part of that council"
+#: templates/web/default/reports/council.html:50 templates/web/default/reports/council.html:52 templates/web/default/reports/council.html:56 templates/web/default/reports/council.html:57 templates/web/default/reports/index.html:12 templates/web/emptyhomes/reports/index.html:12 templates/web/fiksgatami/reports/index.html:11
+msgid "Older problems"
msgstr ""
-#: web/index.cgi:374
-msgid "We have details for that council"
+#: templates/web/default/admin/report_edit.html:22 templates/web/default/admin/update_edit.html:19
+msgid "Open"
msgstr ""
-#: web/index.cgi:383
-msgid "Somehow, you only have one co-ordinate. Please try again."
+#: templates/web/default/alert/list.html:88
+msgid "Or problems reported to:"
msgstr ""
-#: web/index.cgi:385
-msgid "You haven't specified any sort of co-ordinates. Please try again."
+#: templates/web/default/alert/list.html:63
+msgid "Or you can subscribe to an alert based upon what ward or council you&rsquo;re in:"
msgstr ""
-#: web/index.cgi:424
-#, perl-format
-msgid ""
-"You have successfully confirmed your report and you can now <a href=\"%s"
-"\">view it on the site</a>."
+#: bin/send-reports:165 bin/send-reports:174 perllib/FixMyStreet/App/Controller/Report/New.pm:492 perllib/FixMyStreet/App/Controller/Report/New.pm:501 perllib/FixMyStreet/App/Controller/Report/New.pm:856 perllib/FixMyStreet/DB/Result/Problem.pm:316 perllib/FixMyStreet/DB/Result/Problem.pm:326 perllib/FixMyStreet/DB/Result/Problem.pm:336 perllib/FixMyStreet/DB/Result/Problem.pm:348
+msgid "Other"
msgstr ""
-#: web/index.cgi:566
-msgid ""
-"That spot does not appear to be covered by a council.\n"
-"If you have tried to report an issue past the shoreline, for example,\n"
-"please specify the closest point on land."
+#: templates/web/default/errors/page_error_410_gone.html:1 templates/web/default/errors/page_error_410_gone.html:3
+msgid "Page Gone"
msgstr ""
-#: web/index.cgi:580
-msgid "Empty house or bungalow"
+#: templates/web/default/errors/page_error_404_not_found.html:1 templates/web/default/errors/page_error_404_not_found.html:3
+msgid "Page Not Found"
msgstr ""
-#: web/index.cgi:581
-msgid "Empty flat or maisonette"
+#: templates/web/default/admin/report_edit.html:22
+msgid "Partial"
msgstr ""
-#: web/index.cgi:581
-msgid "Whole block of empty flats"
+#: templates/web/default/auth/change_password.html:26 templates/web/default/auth/general.html:33
+msgid "Password:"
msgstr ""
-#: web/index.cgi:582
-msgid "Empty office or other commercial"
+#: bin/send-reports:68 templates/web/default/admin/report_edit.html:29 templates/web/default/report/new/fill_in_details.html:158
+msgid "Phone:"
msgstr ""
-#: web/index.cgi:582
-msgid "Empty pub or bar"
+#: templates/web/default/questionnaire/index.html:97 templates/web/default/report/display.html:125 templates/web/default/report/new/fill_in_details.html:120
+msgid "Photo:"
msgstr ""
-#: web/index.cgi:583
-msgid "Empty public building - school, hospital, etc."
+#: templates/web/default/alert/list.html:29
+msgid "Photos of recent nearby reports"
msgstr ""
-#: web/index.cgi:584
-msgid "Property type:"
+#: templates/web/default/index.html:78
+msgid "Photos of recent reports"
msgstr ""
-#: web/index.cgi:588 web/index.cgi:604 web-admin/index.cgi:629
-msgid "Category:"
+#: templates/web/default/report/new/notes.html:6
+msgid "Please be polite, concise and to the point."
msgstr ""
-#: web/index.cgi:663
-msgid ""
-"Please note your report has <strong>not yet been sent</strong>. Choose a "
-"category and add further information below, then submit."
+#: templates/web/default/auth/change_password.html:12 templates/web/default/auth/change_password.html:17
+msgid "Please check the passwords and try again"
msgstr ""
-#: web/index.cgi:666
-msgid ""
-"You have located the problem at the point marked with a purple pin on the "
-"map.\n"
-"If this is not the correct location, simply click on the map again. "
+#: templates/web/default/auth/token.html:17
+msgid "Please check your email"
msgstr ""
-#: web/index.cgi:672 web/index.cgi:697 web/index.cgi:699 web/index.cgi:712
-msgid " or "
+#: templates/web/default/auth/general.html:14 templates/web/default/auth/general.html:8
+msgid "Please check your email address is correct"
msgstr ""
-#: web/index.cgi:674
-#, perl-format
-msgid ""
-"All the information you provide here will be sent to <strong>%s</strong>.\n"
-"On the site, we will show the subject and details of the problem, plus your\n"
-"name if you give us permission."
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:640 perllib/FixMyStreet/App/Controller/Report/New.pm:660 perllib/FixMyStreet/App/Controller/Report/New.pm:676 perllib/FixMyStreet/DB/Result/Problem.pm:206
+msgid "Please choose a category"
msgstr ""
-#: web/index.cgi:678
-#, perl-format
-msgid ""
-"All the information you\n"
-" provide here will be sent to <strong>%s</strong> or a relevant\n"
-" local body such as TfL, via the London Report-It system. The\n"
-" subject and details of the problem will be public, plus your "
-"name\n"
-" if you give us permission."
+#: perllib/FixMyStreet/DB/Result/Problem.pm:212
+msgid "Please choose a property type"
msgstr ""
-#: web/index.cgi:684
-#, perl-format
+#: templates/web/default/contact/blurb.html:2
msgid ""
-"All the information you provide here will be sent to <strong>%s</strong>.\n"
-"The subject and details of the problem will be public, plus your\n"
-"name if you give us permission."
+"Please do <strong>not</strong> report problems through this form; messages go to\n"
+"the team behind FixMyStreet, not a council. To report a problem,\n"
+"please <a href=\"/\">go to the front page</a> and follow the instructions."
msgstr ""
-#: web/index.cgi:698
-msgid "All the information you provide here will be sent to"
+#: templates/web/default/report/new/notes.html:7
+msgid "Please do not be abusive &mdash; abusing your council devalues the service for all users."
msgstr ""
-#: web/index.cgi:701
-msgid ""
-"The subject and details of the problem will be public, plus your name if you "
-"give us permission."
+#: perllib/FixMyStreet/DB/Result/Comment.pm:108
+msgid "Please enter a message"
msgstr ""
-#: web/index.cgi:703
-msgid ""
-"We do <strong>not</strong> yet have details for the other council that "
-"covers this location."
-msgid_plural ""
-"We do <strong>not</strong> yet have details for the other councils that "
-"cover this location."
-msgstr[0] ""
-msgstr[1] ""
+#: templates/web/default/auth/change_password.html:12 templates/web/default/auth/change_password.html:15
+msgid "Please enter a password"
+msgstr ""
-#: web/index.cgi:707 web/index.cgi:722
-#, perl-format
-msgid ""
-"You can help us by finding a contact email address for local problems for %s "
-"and emailing it to us at <a href='mailto:%s'>%s</a>."
+#: perllib/FixMyStreet/App/Controller/Contact.pm:95 perllib/FixMyStreet/DB/Result/Problem.pm:181
+msgid "Please enter a subject"
msgstr ""
-#: web/index.cgi:717 web/index.cgi:725
-msgid "We do not yet have details for the council that covers this location."
-msgid_plural ""
-"We do not yet have details for the councils that cover this location."
-msgstr[0] ""
-msgstr[1] ""
+#: perllib/FixMyStreet/DB/Result/User.pm:91
+msgid "Please enter a valid email"
+msgstr ""
-#: web/index.cgi:721
-msgid ""
-"If you submit a problem here the subject and details of the problem will be "
-"public, but the problem will <strong>not</strong> be reported to the council."
+#: perllib/FixMyStreet/App/Controller/Alert.pm:123 perllib/FixMyStreet/App/Controller/Contact.pm:105
+msgid "Please enter a valid email address"
msgstr ""
-#: web/index.cgi:727
-msgid ""
-"If you submit a report here it will be left on the site, but not reported to "
-"the council &ndash; please still leave your report, so that we can show to "
-"the council the activity in their area."
+#: perllib/FixMyStreet/DB/Result/Problem.pm:184
+msgid "Please enter some details"
msgstr ""
-#: web/index.cgi:733
-msgid ""
-"Please fill in the form below with details of the problem,\n"
-"and describe the location as precisely as possible in the details box."
+#: perllib/FixMyStreet/App/Controller/Contact.pm:94 perllib/FixMyStreet/DB/Result/User.pm:88 templates/web/default/auth/general.html:13 templates/web/default/auth/general.html:8
+msgid "Please enter your email"
msgstr ""
-#: web/index.cgi:742
+#: perllib/FixMyStreet/DB/Result/Problem.pm:199 perllib/FixMyStreet/DB/Result/User.pm:83
+msgid "Please enter your full name, councils need this information - if you do not wish your name to be shown on the site, untick the box"
+msgstr ""
+
+#: perllib/FixMyStreet/App/Controller/Contact.pm:93 perllib/FixMyStreet/DB/Result/Problem.pm:192 perllib/FixMyStreet/DB/Result/User.pm:76
+msgid "Please enter your name"
+msgstr ""
+
+#: templates/web/emptyhomes/report/new/fill_in_details_text.html:1
msgid ""
"Please fill in details of the empty property below, saying what type of\n"
"property it is e.g. an empty home, block of flats, office etc. Tell us\n"
-"something about its condition and any other information you feel is "
-"relevant.\n"
-"There is no need for you to give the exact address. Please be polite, "
-"concise\n"
-"and to the point; writing your message entirely in block capitals makes it "
-"hard\n"
-"to read, as does a lack of punctuation.\n"
+"something about its condition and any other information you feel is relevant.\n"
+"There is no need for you to give the exact address. Please be polite, concise\n"
+"and to the point; writing your message entirely in block capitals makes it hard\n"
+"to read, as does a lack of punctuation."
+msgstr ""
+
+#: templates/web/default/report/new/fill_in_details_text.html:1 templates/web/default/report/new/fill_in_details_text.html:8
+msgid "Please fill in details of the problem below."
msgstr ""
-#: web/index.cgi:750
+#: templates/web/default/report/new/fill_in_details_text.html:1 templates/web/default/report/new/fill_in_details_text.html:3
msgid ""
"Please fill in details of the problem below. The council won't be able\n"
-"to help unless you leave as much detail as you can, so please describe the "
-"exact location of\n"
-"the problem (e.g. on a wall), what it is, how long it has been there, a "
-"description (and a\n"
+"to help unless you leave as much detail as you can, so please describe the exact location of\n"
+"the problem (e.g. on a wall), what it is, how long it has been there, a description (and a\n"
"photo of the problem if you have one), etc."
msgstr ""
-#: web/index.cgi:755
-msgid "Please fill in details of the problem below."
-msgstr ""
-
-#: web/index.cgi:764
-msgid "Empty property details form"
+#: templates/web/default/report/new/fill_in_details.html:49
+msgid "Please fill in the form below with details of the problem, and describe the location as precisely as possible in the details box."
msgstr ""
-#: web/index.cgi:766 web-admin/index.cgi:624
-msgid "Details:"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:239
+msgid "Please indicate whether you'd like to receive another questionnaire"
msgstr ""
-#: web/index.cgi:767 web/index.cgi:1143 web/questionnaire.cgi:308
-msgid "Photo:"
+#: templates/web/default/report/display.html:69
+msgid "Please note that updates are not sent to the council. If you leave your name it will be public. Your information will only be used in accordance with our <a href=\"/faq#privacy\">privacy policy</a>"
msgstr ""
-#: web/index.cgi:768 web/index.cgi:1124 web-admin/index.cgi:630
-#: web-admin/index.cgi:750
-msgid "Name:"
+#: templates/web/default/report/new/fill_in_details.html:34
+msgid "Please note your report has <strong>not yet been sent</strong>. Choose a category and add further information below, then submit."
msgstr ""
-#: web/index.cgi:770 bin/send-reports:94 web-admin/index.cgi:632
-msgid "Phone:"
+#: templates/web/default/report/new/notes.html:1
+msgid "Please note:"
msgstr ""
-#: web/index.cgi:771 web/index.cgi:1131
-msgid "(optional)"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:242
+msgid "Please provide some explanation as to why you're reopening this report"
msgstr ""
-#: web/index.cgi:773
-msgid "Can we show your name on the site?"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:249
+msgid "Please provide some text as well as a photo"
msgstr ""
-#: web/index.cgi:775
-msgid "Can we show your name publicly?"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:119 perllib/FixMyStreet/App/Controller/Questionnaire.pm:235
+msgid "Please say whether you've ever reported a problem to your council before"
msgstr ""
-#: web/index.cgi:777
-msgid "(we never show your email address or phone number)"
+#: perllib/FixMyStreet/App/Controller/Alert.pm:85
+msgid "Please select the feed you want"
msgstr ""
-#: web/index.cgi:804
-msgid "Please note:"
+#: perllib/FixMyStreet/App/Controller/Alert.pm:125
+msgid "Please select the type of alert you want"
msgstr ""
-#: web/index.cgi:806
-msgid ""
-"We will only use your personal information in accordance with our <a href=\"/"
-"faq#privacy\">privacy policy.</a>"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:231
+msgid "Please state whether or not the problem has been fixed"
msgstr ""
-#: web/index.cgi:807
-msgid "Please be polite, concise and to the point."
+#: templates/web/default/questionnaire/index.html:52
+msgid "Please take a look at the updates that have been left."
msgstr ""
-#: web/index.cgi:808
-msgid ""
-"Please do not be abusive &mdash; abusing your council devalues the service "
-"for all users."
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:729
+msgid "Please upload a JPEG image only"
msgstr ""
-#: web/index.cgi:809
-msgid ""
-"Writing your message entirely in block capitals makes it hard to read, as "
-"does a lack of punctuation."
+#: perllib/FixMyStreet/App/Controller/Contact.pm:96
+msgid "Please write a message"
msgstr ""
-#: web/index.cgi:810
-msgid ""
-"Remember that FixMyStreet is primarily for reporting physical problems that "
-"can be fixed. If your problem is not appropriate for submission via this "
-"site remember that you can contact your council directly using their own "
-"website."
+#: templates/web/default/contact/index.html:93 templates/web/default/report/display.html:138
+msgid "Post"
msgstr ""
-#: web/index.cgi:812
-msgid ""
-"FixMyStreet and the Guardian are providing this service in partnership in <a "
-"href=\"/faq#privacy\">certain cities</a>. In those cities, both have access "
-"to any information submitted, including names and email addresses, and will "
-"use it only to ensure the smooth running of the service, in accordance with "
-"their privacy policies."
+#: templates/web/default/report/updates.html:8
+msgid "Posted anonymously at %s"
msgstr ""
-#: web/index.cgi:916
-msgid "Hide stale reports"
+#: templates/web/default/report/updates.html:10
+msgid "Posted by %s at %s"
msgstr ""
-#: web/index.cgi:919
-msgid "Include stale reports"
+#: perllib/FixMyStreet/Map/Tilma/Original.pm:90 templates/web/default/maps/openlayers.html:81 templates/web/default/maps/tilma/original.html:62
+msgid "Problem"
msgstr ""
-#: web/index.cgi:938
-msgid "No problems have been reported yet."
+#: templates/web/default/admin/timeline.html:24
+msgid "Problem %d created"
msgstr ""
-#: web/index.cgi:954
-msgid "No problems found."
+#: templates/web/default/admin/timeline.html:26
+msgid "Problem %s confirmed"
msgstr ""
-#: web/index.cgi:959
-msgid "Show pins"
+#: templates/web/default/admin/timeline.html:28
+msgid "Problem %s sent to council %s"
msgstr ""
-#: web/index.cgi:963
-msgid "Hide pins"
+#: templates/web/default/admin/index.html:27
+msgid "Problem breakdown by state"
msgstr ""
-#: web/index.cgi:1004
-msgid "Email me new local problems"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:745
+msgid "Problem marked as open."
msgstr ""
-#: web/index.cgi:1006
-msgid "RSS feed of recent local problems"
+#: templates/web/default/around/display_location.html:73
+msgid "Problems in this area"
msgstr ""
-#: web/index.cgi:1009
-msgid "Problems in this area"
+#: db/alert_types.pl:14
+msgid "Problems recently reported fixed on FixMyStreet"
msgstr ""
-#: web/index.cgi:1010
-msgid "Reports on and around the map"
+#: templates/web/default/alert/list.html:52
+msgid "Problems within %.1fkm of this location"
msgstr ""
-#: web/index.cgi:1011
-#, perl-format
-msgid "Closest nearby problems <small>(within&nbsp;%skm)</small>"
+#: perllib/FixMyStreet/Cobrand/Default.pm:694 perllib/FixMyStreet/Cobrand/EmptyHomes.pm:161 perllib/FixMyStreet/Cobrand/FiksGataMi.pm:123
+msgid "Problems within %s"
msgstr ""
-#: web/index.cgi:1015
-msgid ""
-"To report a problem, simply\n"
-" <strong>click on the map</strong> at the correct location."
+#: perllib/FixMyStreet/Cobrand/Default.pm:703 perllib/FixMyStreet/Cobrand/EmptyHomes.pm:169
+msgid "Problems within %s ward"
msgstr ""
-#: web/index.cgi:1017
-#, perl-format
-msgid ""
-"<small>If you cannot see the map, <a href='%s' rel='nofollow'>skip this\n"
-" step</a>.</small>"
+#: templates/web/default/reports/council.html:11 templates/web/default/reports/council.html:14
+msgid "Problems within %s, FixMyStreet"
msgstr ""
-#: web/index.cgi:1022
-msgid "Recent local problems, FixMyStreet"
+#: templates/web/default/alert/list.html:69
+msgid "Problems within the boundary of:"
msgstr ""
-#: web/index.cgi:1035
-msgid "There were problems with your update. Please see below."
+#: db/alert_types_eha.pl:8
+msgid "Properties recently reported as put back to use on reportemptyhomes.com"
msgstr ""
-#: web/index.cgi:1056 web/index.cgi:1058
-msgid "Unknown problem ID"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:469
+msgid "Property type:"
msgstr ""
-#: web/index.cgi:1059
-msgid "That report has been removed from FixMyStreet."
+#: templates/web/default/report/display.html:64
+msgid "Provide an update"
msgstr ""
-#: web/index.cgi:1079
-msgid "This problem is old and of unknown status."
+#: templates/web/default/questionnaire/index.html:0 templates/web/default/questionnaire/index.html:14 templates/web/default/questionnaire/index.html:4
+msgid "Questionnaire"
msgstr ""
-#: web/index.cgi:1082 web/index.cgi:1123
-msgid "This problem has been fixed"
+#: templates/web/default/admin/timeline.html:32
+msgid "Questionnaire %d answered for problem %d, %s to %s"
msgstr ""
-#: web/index.cgi:1107
-msgid "More problems nearby"
+#: templates/web/default/admin/timeline.html:30
+msgid "Questionnaire %d sent for problem %d"
msgstr ""
-#: web/index.cgi:1110
-msgid "Email me updates"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:185
+msgid "Questionnaire filled in by problem reporter"
msgstr ""
-#: web/index.cgi:1113
-msgid "Receive email when updates are left on this problem"
+#: templates/web/default/alert/list.html:54 templates/web/default/around/display_location.html:1 templates/web/default/around/display_location.html:3 templates/web/default/report/display.html:55 templates/web/default/reports/council.html:17
+msgid "RSS feed"
msgstr ""
-#: web/index.cgi:1118
-msgid "RSS feed of updates to this problem"
+#: perllib/FixMyStreet/Cobrand/Default.pm:732 perllib/FixMyStreet/Cobrand/Default.pm:746
+msgid "RSS feed for %s"
msgstr ""
-#: web/index.cgi:1119
-msgid "Provide an update"
+#: perllib/FixMyStreet/Cobrand/Default.pm:739 perllib/FixMyStreet/Cobrand/Default.pm:753
+msgid "RSS feed for %s ward, %s"
msgstr ""
-#: web/index.cgi:1125
-msgid "Update:"
+#: perllib/FixMyStreet/Cobrand/Default.pm:762 perllib/FixMyStreet/Cobrand/Default.pm:776 perllib/FixMyStreet/Cobrand/FiksGataMi.pm:139 perllib/FixMyStreet/Cobrand/FiksGataMi.pm:147 perllib/FixMyStreet/Cobrand/FiksGataMi.pm:157 perllib/FixMyStreet/Cobrand/FiksGataMi.pm:165
+msgid "RSS feed of %s"
msgstr ""
-#: web/index.cgi:1126
-msgid "Alert me to future updates"
+#: perllib/FixMyStreet/Cobrand/Default.pm:768 perllib/FixMyStreet/Cobrand/Default.pm:782
+msgid "RSS feed of %s, within %s ward"
msgstr ""
-#: web/index.cgi:1134
-msgid ""
-"Please note that updates are not sent to the council. If you leave your name "
-"it will be public. Your information will only be used in accordance with our "
-"<a href=\"/faq#privacy\">privacy policy</a>"
+#: templates/web/default/alert/list.html:54
+msgid "RSS feed of nearby problems"
msgstr ""
-#: web/index.cgi:1154
-msgid "Updates to this problem, FixMyStreet"
+#: templates/web/default/reports/council.html:17
+msgid "RSS feed of problems in this %s"
msgstr ""
-#: web/questionnaire.cgi:29 web/questionnaire.cgi:80 web/questionnaire.cgi:213
-#: web/questionnaire.cgi:233
-msgid "Questionnaire"
+#: perllib/FixMyStreet/Cobrand/Default.pm:695 perllib/FixMyStreet/Cobrand/EmptyHomes.pm:162 perllib/FixMyStreet/Cobrand/FiksGataMi.pm:122
+msgid "RSS feed of problems within %s"
msgstr ""
-#: web/questionnaire.cgi:41
-msgid ""
-"I'm afraid we couldn't validate that token. If you've copied the URL from an "
-"email, please check that you copied it exactly.\n"
+#: perllib/FixMyStreet/Cobrand/Default.pm:702 perllib/FixMyStreet/Cobrand/EmptyHomes.pm:168
+msgid "RSS feed of problems within %s ward"
msgstr ""
-#: web/questionnaire.cgi:48
-#, perl-format
-msgid ""
-"You have already answered this questionnaire. If you have a question, please "
-"<a href='%s'>get in touch</a>, or <a href='%s'>view your problem</a>.\n"
+#: templates/web/default/around/display_location.html:1 templates/web/default/around/display_location.html:4
+msgid "RSS feed of recent local problems"
msgstr ""
-#: web/questionnaire.cgi:53
-msgid "I'm afraid we couldn't locate your problem in the database.\n"
+#: templates/web/default/report/display.html:55
+msgid "RSS feed of updates to this problem"
msgstr ""
-#: web/questionnaire.cgi:92
-msgid "Please state whether or not the problem has been fixed"
+#: templates/web/default/alert/updates.html:9 templates/web/default/report/display.html:45
+msgid "Receive email when updates are left on this problem."
msgstr ""
-#: web/questionnaire.cgi:95
-msgid ""
-"Please say whether you've ever reported a problem to your council before"
+#: templates/web/default/around/display_location.html:0 templates/web/default/around/display_location.html:35
+msgid "Recent local problems, FixMyStreet"
msgstr ""
-#: web/questionnaire.cgi:97
-msgid "Please indicate whether you'd like to receive another questionnaire"
+#: templates/web/default/reports/council.html:68 templates/web/default/reports/council.html:69 templates/web/default/reports/index.html:14 templates/web/emptyhomes/reports/index.html:13 templates/web/fiksgatami/reports/index.html:13
+msgid "Recently fixed"
msgstr ""
-#: web/questionnaire.cgi:99
-msgid "Please provide some explanation as to why you're reopening this report"
+#: templates/web/default/index.html:87
+msgid "Recently reported problems"
msgstr ""
-#: web/questionnaire.cgi:115
-msgid "Please provide some text as well as a photo"
+#: templates/web/default/report/new/notes.html:9
+msgid "Remember that FixMyStreet is primarily for reporting physical problems that can be fixed. If your problem is not appropriate for submission via this site remember that you can contact your council directly using their own website."
msgstr ""
-#: web/questionnaire.cgi:146
-msgid "Questionnaire filled in by problem reporter"
+#: templates/web/default/admin/report_edit.html:43 templates/web/default/admin/update_edit.html:33
+msgid "Remove photo (can't be undone!)"
msgstr ""
-#: web/questionnaire.cgi:166
-msgid ""
-"<p>Thank you very much for filling in our questionnaire; if you\n"
-"get some more information about the status of your problem, please come back "
-"to the\n"
-"site and leave an update.</p>\n"
+#: perllib/FixMyStreet/Cobrand/EmptyHomes.pm:77
+msgid "Report Empty Homes"
msgstr ""
-#: web/questionnaire.cgi:174
-#, perl-format
-msgid ""
-"<p style=\"font-size:150%%\">We're sorry to hear that. We have two "
-"suggestions: why not try\n"
-"<a href=\"%s\">writing direct to your councillor(s)</a>\n"
-"or, if it's a problem that could be fixed by local people working together,\n"
-"why not <a href=\"http://www.pledgebank.com/new\">make and publicise a "
-"pledge</a>?\n"
-"</p>\n"
+#: templates/web/default/footer.html:6 templates/web/emptyhomes/header.html:30 templates/web/fiksgatami/footer.html:5
+msgid "Report a problem"
msgstr ""
-#: web/questionnaire.cgi:183
-msgid ""
-"<p style=\"font-size:150%\">Thank you very much for filling in our "
-"questionnaire; glad to hear it's been fixed.</p>\n"
+#: perllib/FixMyStreet/App/Controller/Rss.pm:262
+msgid "Report on %s"
msgstr ""
-#: web/questionnaire.cgi:236
-msgid "Don&rsquo;t know"
+#: templates/web/default/index.html:11
+msgid "Report, view, or discuss local problems"
msgstr ""
-#: web/questionnaire.cgi:237
-msgid "Submit questionnaire"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:331 templates/web/default/contact/index.html:45
+msgid "Reported anonymously at %s"
msgstr ""
-#: web/questionnaire.cgi:254
-msgid ""
-"<p>Getting empty homes back into use can be difficult. You shouldn't expect\n"
-"the property to be back into use yet. But a good council will have started "
-"work\n"
-"and should have reported what they have done on the website. If you are not\n"
-"satisfied with progress or information from the council, now is the right "
-"time\n"
-"to say. You may also want to try contacting some other people who may be "
-"able\n"
-"to help. For advice on how to do this and other useful information please\n"
-"go to <a href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://"
-"www.emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
+#: templates/web/default/admin/questionnaire.html:5 templates/web/default/questionnaire/index.html:79
+msgid "Reported before"
msgstr ""
-#: web/questionnaire.cgi:264
-msgid ""
-"<p>Getting empty homes back into use can be difficult, but by now a good "
-"council\n"
-"will have made a lot of progress and reported what they have done on the\n"
-"website. Even so properties can remain empty for many months if the owner "
-"is\n"
-"unwilling or the property is in very poor repair. If nothing has happened "
-"or\n"
-"you are not satisfied with the progress the council is making, now is the "
-"right\n"
-"time to say so. We think it's a good idea to contact some other people who\n"
-"may be able to help or put pressure on the council For advice on how to do\n"
-"this and other useful information please go to <a\n"
-"href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://www."
-"emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:323
+msgid "Reported by %s anonymously at %s"
msgstr ""
-#: web/questionnaire.cgi:277
-msgid ""
-"The details of your problem are available on the right hand side of this "
-"page."
+#: perllib/FixMyStreet/DB/Result/Problem.pm:354 templates/web/default/contact/index.html:47
+msgid "Reported by %s at %s"
msgstr ""
-#: web/questionnaire.cgi:278
-msgid "Please take a look at the updates that have been left."
+#: perllib/FixMyStreet/DB/Result/Problem.pm:345
+msgid "Reported by %s by %s at %s"
msgstr ""
-#: web/questionnaire.cgi:284
-msgid "An update marked this problem as fixed."
+#: perllib/FixMyStreet/DB/Result/Problem.pm:319
+msgid "Reported by %s in the %s category anonymously at %s"
msgstr ""
-#: web/questionnaire.cgi:285
-msgid "Has this problem been fixed?"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:339
+msgid "Reported by %s in the %s category by %s at %s"
msgstr ""
-#: web/questionnaire.cgi:292 web-admin/index.cgi:889
-msgid "Reported before"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:327
+msgid "Reported in the %s category anonymously at %s"
msgstr ""
-#: web/questionnaire.cgi:293
-msgid "First time"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:349
+msgid "Reported in the %s category by %s at %s"
msgstr ""
-#: web/questionnaire.cgi:294
-msgid ""
-"Have you ever reported a problem to a council before, or is this your first "
-"time?"
+#: templates/web/default/around/around_index.html:1 templates/web/default/report/new/fill_in_details.html:0 templates/web/default/report/new/fill_in_details.html:3 templates/web/default/report/new/fill_in_details.html:30
+msgid "Reporting a problem"
msgstr ""
-#: web/questionnaire.cgi:304
-msgid ""
-"If you wish to leave a public update on the problem, please enter it here\n"
-"(please note it will not be sent to the council). For example, what was\n"
-"your experience of getting the problem fixed?"
+#: templates/web/default/around/display_location.html:103
+msgid "Reports on and around the map"
msgstr ""
-#: web/questionnaire.cgi:322
-msgid ""
-"Would you like to receive another questionnaire in 4 weeks, reminding you to "
-"check the status?"
+#: templates/web/default/admin/report_edit.html:32
+msgid "Resend report"
msgstr ""
-#: web/reports.cgi:192 web/reports.cgi:239
-msgid "Summary reports"
+#: templates/web/default/admin/council_edit.html:35
+msgid "Save changes"
msgstr ""
-#: web/reports.cgi:194
-msgid ""
-"This is a summary of all reports on this site; select a particular council "
-"to see the reports sent there."
+#: perllib/FixMyStreet/App/Controller/Admin.pm:599 templates/web/default/admin/search_reports.html:1
+msgid "Search Reports"
msgstr ""
-#: web/reports.cgi:195
-msgid "Greyed-out lines are councils that no longer exist."
+#: templates/web/default/admin/search_reports.html:5
+msgid "Search:"
msgstr ""
-#: web/reports.cgi:199 web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "Name"
+#: templates/web/default/alert/list.html:41
+msgid "Select which type of alert you'd like and click the button for an RSS feed, or enter your email address to subscribe to an email alert."
msgstr ""
-#: web/reports.cgi:199 web/reports.cgi:275
-msgid "New problems"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:378
+msgid "Sent to %s %s later"
msgstr ""
-#: web/reports.cgi:199 web/reports.cgi:283
-msgid "Older problems"
+#: templates/web/default/admin/report_edit.html:32
+msgid "Sent:"
msgstr ""
-#: web/reports.cgi:201
-msgid "Old problems,<br>state unknown"
+#: templates/web/default/admin/report_edit.html:34
+msgid "Service:"
msgstr ""
-#: web/reports.cgi:203 web/reports.cgi:290
-msgid "Recently fixed"
+#: templates/web/emptyhomes/static/about.html:21
+msgid "Shelter Cymru"
msgstr ""
-#: web/reports.cgi:203
-msgid "Older fixed"
+#: templates/web/emptyhomes/static/about.html:23
+msgid ""
+"Shelter Cymru is Wales&rsquo; people and homes charity and wants\n"
+" everyone in Wales to have a decent home. We believe a home is a fundamental\n"
+" right and essential to the health and well-being of people and communities.\n"
+" We work for people in housing need. We have offices all over Wales and\n"
+" prevent people from losing their homes by offering free, confidential and\n"
+" independent advice. When necessary we constructively challenge on behalf of\n"
+" people to ensure they are properly assisted and to improve practice and\n"
+" learning. We believe that bringing empty homes back into use can make a\n"
+" significant contribution to the supply of affordable homes in Wales."
msgstr ""
-#: web/reports.cgi:245
-msgid "council"
+#: templates/web/default/around/display_location.html:52
+msgid "Show pins"
msgstr ""
-#: web/reports.cgi:255
-#, perl-format
-msgid "RSS feed of problems in this %s"
+#: templates/web/default/alert/index.html:31
+msgid "Some photos of recent reports"
msgstr ""
-#: web/reports.cgi:259
-#, perl-format
-msgid "This is a summary of all reports for one %s."
+#: perllib/FixMyStreet/App/View/Email.pm:32 perllib/FixMyStreet/App/View/Web.pm:38
+msgid "Some text to localize"
msgstr ""
-#: web/reports.cgi:260
-#, perl-format
-msgid "This is a summary of all reports for this %s."
+#: templates/web/default/admin/council_list.html:42
+msgid "Some unconfirmeds"
msgstr ""
-#: web/reports.cgi:264
-#, perl-format
-msgid "You can <a href=\"%s\">see less detail</a>."
+#: perllib/FixMyStreet/Geocode.pm:171
+msgid "Sorry, that appears to be a Crown dependency postcode, which we don't cover."
msgstr ""
-#: web/reports.cgi:266
-#, perl-format
-msgid "You can <a href=\"%s\">see more details</a>."
+#: perllib/FixMyStreet/Geocode.pm:71
+msgid "Sorry, that location appears to be too general; please be more specific."
msgstr ""
-#: web/reports.cgi:268
-#, perl-format
-msgid ""
-"You can <a href=\"%s\">see less detail</a> or go back and <a href=\"/reports"
-"\">show all councils</a>."
+#: templates/web/default/tokens/abuse.html:5
+msgid "Sorry, there has been an error confirming your problem."
msgstr ""
-#: web/reports.cgi:270
-#, perl-format
-msgid ""
-"You can <a href=\"%s\">see more details</a> or go back and <a href=\"/reports"
-"\">show all councils</a>."
+#: perllib/FixMyStreet/Geocode.pm:103 perllib/FixMyStreet/Geocode.pm:150
+msgid "Sorry, we could not find that location."
msgstr ""
-#: web/reports.cgi:285
-msgid "Old problems, state unknown"
+#: perllib/FixMyStreet/Geocode.pm:148
+msgid "Sorry, we could not parse that location. Please try again."
msgstr ""
-#: web/reports.cgi:291
-msgid "Old fixed"
+#: templates/web/default/admin/list_updates.html:6 templates/web/default/admin/search_reports.html:21
+msgid "State"
msgstr ""
-#: web/reports.cgi:294
-#, perl-format
-msgid "%s - Summary reports"
+#: templates/web/default/admin/report_edit.html:21 templates/web/default/admin/update_edit.html:18
+msgid "State:"
msgstr ""
-#: web/reports.cgi:294
-#, perl-format
-msgid "Problems within %s, FixMyStreet"
+#: templates/web/default/admin/report_edit.html:13 templates/web/default/contact/index.html:83 templates/web/default/report/new/fill_in_details.html:91
+msgid "Subject:"
msgstr ""
-#: web/reports.cgi:333
-msgid "(sent to both)"
+#: templates/web/default/questionnaire/creator_fixed.html:19 templates/web/default/report/new/fill_in_details.html:171
+msgid "Submit"
msgstr ""
-#: web/reports.cgi:334
-msgid "(not sent to council)"
+#: templates/web/default/admin/report_edit.html:46 templates/web/default/admin/update_edit.html:36
+msgid "Submit changes"
msgstr ""
-#: bin/send-reports:96
-msgid ""
-"This web page also contains a photo of the problem, provided by the user."
+#: templates/web/default/questionnaire/index.html:114
+msgid "Submit questionnaire"
msgstr ""
-#: bin/send-reports:102
-msgid "To view a map of the precise location of this issue"
+#: templates/web/default/alert/updates.html:17 templates/web/default/report/display.html:50
+msgid "Subscribe"
msgstr ""
-#: bin/send-reports:103
-msgid ""
-"The user could not locate the problem on a map, but to see the area around "
-"the location they entered"
+#: templates/web/default/alert/list.html:127
+msgid "Subscribe me to an email alert"
msgstr ""
-#: bin/send-reports:187
-msgid "this type of local problem"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:597 templates/web/default/admin/index.html:1
+msgid "Summary"
msgstr ""
-#: bin/send-reports:191
-#, perl-format
-msgid "Category: %s"
+#: templates/web/default/reports/index.html:1 templates/web/emptyhomes/reports/index.html:1 templates/web/fiksgatami/reports/index.html:1
+msgid "Summary reports"
msgstr ""
-#: bin/send-reports:194
-msgid " and "
+#: perllib/FixMyStreet/App/Controller/Admin.pm:601 templates/web/default/admin/questionnaire.html:1
+msgid "Survey Results"
msgstr ""
-#: bin/send-reports:196
-msgid ""
-"This email has been sent to both councils covering the location of the "
-"problem, as the user did not categorise it; please ignore it if you're not "
-"the correct council to deal with the issue, or let us know what category of "
-"problem this is so we can add it to our system."
+#: templates/web/default/admin/list_updates.html:12
+msgid "Text"
msgstr ""
-#: bin/send-reports:199
-msgid ""
-"This email has been sent to several councils covering the location of the "
-"problem, as the category selected is provided for all of them; please ignore "
-"it if you're not the correct council to deal with the issue."
+#: templates/web/default/admin/council_contacts.html:12
+msgid "Text only version"
msgstr ""
-#: bin/send-reports:206
-#, perl-format
-msgid ""
-"We realise this problem might be the responsibility of %s; however, we don't "
-"currently have any contact details for them. If you know of an appropriate "
-"contact address, please do get in touch."
+#: templates/web/default/admin/update_edit.html:13
+msgid "Text:"
msgstr ""
-#: db/alert_types_eha.pl:5
-msgid "New reports on reportemptyhomes.com"
+#: templates/web/default/tokens/confirm_update.html:7 templates/web/default/tokens/confirm_update.html:8
+msgid "Thank you &mdash; you can <a href=\"%s\">view your updated problem</a> on the site."
msgstr ""
-#: db/alert_types_eha.pl:8
-msgid "Properties recently reported as put back to use on reportemptyhomes.com"
+#: templates/web/emptyhomes/tokens/confirm_problem.html:6 templates/web/emptyhomes/tokens/confirm_problem.html:8
+msgid "Thank you for reporting an empty property on ReportEmptyHomes.com. We have emailed the lead officer for empty homes in the council responsible with details, and asked them to do whatever they can to get the empty property back into use as soon as possible."
msgstr ""
-#: db/alert_types_eha.pl:9
-msgid "The latest properties reported back to use by users"
+#: templates/web/emptyhomes/tokens/confirm_problem.html:30 templates/web/emptyhomes/tokens/confirm_problem.html:31
+msgid ""
+"Thank you for reporting this empty property on ReportEmptyHomes.com.\n"
+"At present the report cannot be sent through to the council for this area. We\n"
+"are working with councils to link them into the system so that as many areas\n"
+"as possible will be covered."
msgstr ""
-#: db/alert_types_eha.pl:12
-msgid "New local reports on reportemptyhomes.com"
+#: templates/web/default/tokens/error.html:7
+msgid "Thank you for trying to confirm your update or problem. We seem to have an error ourselves though, so <a href=\"%s\">please let us know what went on</a> and we'll look into it."
msgstr ""
-#: db/alert_types_eha.pl:13
-msgid "The latest local reports reported by users"
+#: templates/web/emptyhomes/tokens/confirm_problem.html:24 templates/web/emptyhomes/tokens/confirm_problem.html:26
+msgid "Thank you for using ReportEmptyHomes.com. Your action is already helping to resolve the UK&rsquo;s empty homes crisis."
msgstr ""
-#: db/alert_types_eha.pl:16
-#, perl-brace-format
-msgid "New reports on reportemptyhomes.com near {{POSTCODE}}"
+#: templates/web/default/around/around_index.html:44
+msgid "Thanks for uploading your photo. We now need to locate your problem, so please enter a nearby street name or postcode in the box below&nbsp;:"
msgstr ""
-#: db/alert_types_eha.pl:19
-#, perl-brace-format
-msgid "New reports to {{COUNCIL}} on reportemptyhomes.com"
+#: templates/web/default/contact/submit.html:8
+msgid "Thanks for your feedback. We'll get back to you as soon as we can!"
msgstr ""
-#: db/alert_types_eha.pl:20
-#, perl-brace-format
-msgid "The latest reports for {{COUNCIL}} reported by users"
+#: templates/web/default/questionnaire/creator_fixed.html:9
+msgid "Thanks, glad to hear it's been fixed! Could we just ask if you have ever reported a problem to a council before?"
msgstr ""
-#: db/alert_types_eha.pl:23
-#, perl-brace-format
-msgid ""
-"New reports for {{COUNCIL}} within {{WARD}} ward on reportemptyhomes.com"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:738
+msgid "That image doesn't appear to have uploaded correctly (%s), please try again."
msgstr ""
-#: db/alert_types_eha.pl:24
-#, perl-brace-format
-msgid ""
-"The latest reports for {{COUNCIL}} within {{WARD}} ward reported by users"
+#: templates/web/default/alert/index.html:12
+msgid "That location does not appear to be covered by a council, perhaps it is offshore - please try somewhere more specific."
msgstr ""
-#: db/alert_types_eha.pl:27
-#, perl-brace-format
-msgid "New reports within {{NAME}}'s boundary on reportemptyhomes.com"
+#: perllib/FixMyStreet/Geocode.pm:81
+msgid "That location does not appear to be in Britain; please try again."
msgstr ""
-#: db/alert_types_eha.pl:28
-#, perl-brace-format
-msgid "The latest reports within {{NAME}}'s boundary reported by users"
+#: perllib/FixMyStreet/Geocode.pm:165
+msgid "That postcode was not recognised, sorry."
msgstr ""
-#: db/alert_types.pl:5 db/alert_types.pl:6
-#, perl-brace-format
-msgid "Updates on {{title}}"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:498
+msgid "That problem will now be resent."
msgstr ""
-#: db/alert_types.pl:7
-#, perl-brace-format
-msgid "Update by {{name}}"
+#: perllib/FixMyStreet/App/Controller/Report.pm:75 perllib/FixMyStreet/App/Controller/Rss.pm:41
+msgid "That report has been removed from FixMyStreet."
msgstr ""
-#: db/alert_types.pl:10
-msgid "New problems on FixMyStreet"
+#: templates/web/default/around/around_index.html:27
+msgid "That spot does not appear to be covered by a council. If you have tried to report an issue past the shoreline, for example, please specify the closest point on land."
msgstr ""
-#: db/alert_types.pl:11
-msgid "The latest problems reported by users"
+#: templates/web/emptyhomes/static/about.html:7
+msgid "The Empty Homes Agency"
msgstr ""
-#: db/alert_types.pl:14
-msgid "Problems recently reported fixed on FixMyStreet"
+#: templates/web/emptyhomes/static/about.html:9
+msgid ""
+"The Empty Homes agency is an independent campaigning charity. We\n"
+" are not part of government, and have no formal links with local councils\n"
+" although we work in cooperation with both. We exist to highlight the waste\n"
+" of empty property and work with others to devise and promote sustainable\n"
+" solutions to bring empty property back into use. We are based in London but\n"
+" work across England. We also work in partnership with other charities across\n"
+" the UK."
msgstr ""
-#: db/alert_types.pl:15
-msgid "The latest problems reported fixed by users"
+#: templates/web/default/email_sent.html:26
+msgid "The confirmation email <strong>may</strong> take a few minutes to arrive &mdash; <em>please</em> be patient."
msgstr ""
-#: db/alert_types.pl:18 db/alert_types.pl:22
-msgid "New local problems on FixMyStreet"
+#: templates/web/default/questionnaire/index.html:51
+msgid "The details of your problem are available on the right hand side of this page."
msgstr ""
-#: db/alert_types.pl:19 db/alert_types.pl:23 db/alert_types.pl:27
-#: db/alert_types.pl:31
+#: db/alert_types.pl:19 db/alert_types.pl:23 db/alert_types.pl:27 db/alert_types.pl:31
msgid "The latest local problems reported by users"
msgstr ""
-#: db/alert_types.pl:26
-#, perl-brace-format
-msgid "New problems near {{POSTCODE}} on FixMyStreet"
+#: db/alert_types_eha.pl:13
+msgid "The latest local reports reported by users"
msgstr ""
-#: db/alert_types.pl:30
-#, perl-brace-format
-msgid "New problems NEAR {{POSTCODE}} on FixMyStreet"
+#: db/alert_types.pl:35
+msgid "The latest problems for {{COUNCIL}} reported by users"
msgstr ""
-#: db/alert_types.pl:34
-#, perl-brace-format
-msgid "New problems to {{COUNCIL}} on FixMyStreet"
+#: db/alert_types.pl:39
+msgid "The latest problems for {{COUNCIL}} within {{WARD}} ward reported by users"
msgstr ""
-#: db/alert_types.pl:35
-#, perl-brace-format
-msgid "The latest problems for {{COUNCIL}} reported by users"
+#: db/alert_types.pl:11
+msgid "The latest problems reported by users"
msgstr ""
-#: db/alert_types.pl:38
-#, perl-brace-format
-msgid "New problems for {{COUNCIL}} within {{WARD}} ward on FixMyStreet"
+#: db/alert_types.pl:15
+msgid "The latest problems reported fixed by users"
msgstr ""
-#: db/alert_types.pl:39
-#, perl-brace-format
-msgid ""
-"The latest problems for {{COUNCIL}} within {{WARD}} ward reported by users"
+#: db/alert_types.pl:43
+msgid "The latest problems within {{NAME}}'s boundary reported by users"
msgstr ""
-#: db/alert_types.pl:42
-#, perl-brace-format
-msgid "New problems within {{NAME}}"
+#: db/alert_types_eha.pl:9
+msgid "The latest properties reported back to use by users"
msgstr ""
-#: db/alert_types.pl:43
-#, perl-brace-format
-msgid "The latest problems within {{NAME}}"
+#: db/alert_types_eha.pl:20
+msgid "The latest reports for {{COUNCIL}} reported by users"
msgstr ""
-#: web-admin/index.cgi:68 web-admin/index.cgi:124 web-admin/index.cgi:125
-msgid "Summary"
+#: db/alert_types_eha.pl:24
+msgid "The latest reports for {{COUNCIL}} within {{WARD}} ward reported by users"
msgstr ""
-#: web-admin/index.cgi:69 web-admin/index.cgi:183 web-admin/index.cgi:184
-msgid "Council contacts"
+#: db/alert_types_eha.pl:28
+msgid "The latest reports within {{NAME}}'s boundary reported by users"
msgstr ""
-#: web-admin/index.cgi:70 web-admin/index.cgi:465
-msgid "Search Reports"
+#: templates/web/default/auth/change_password.html:12 templates/web/default/auth/change_password.html:16
+msgid "The passwords do not match"
msgstr ""
-#: web-admin/index.cgi:71 web-admin/index.cgi:784 web-admin/index.cgi:785
-msgid "Timeline"
+#: templates/web/default/errors/page_error_410_gone.html:10 templates/web/default/errors/page_error_410_gone.html:12
+msgid "The requested URL '%s' is no longer available"
msgstr ""
-#: web-admin/index.cgi:72 web-admin/index.cgi:871 web-admin/index.cgi:872
-msgid "Survey Results"
+#: templates/web/default/errors/page_error_404_not_found.html:10 templates/web/default/errors/page_error_404_not_found.html:12
+msgid "The requested URL '%s' was not found on this server"
msgstr ""
-#: web-admin/index.cgi:85
-msgid "FixMyStreet administration"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:479 perllib/FixMyStreet/App/Controller/Admin.pm:649 perllib/FixMyStreet/App/Controller/Admin.pm:688 perllib/FixMyStreet/App/Controller/Admin.pm:773
+msgid "The requested URL was not found on this server."
msgstr ""
-#: web-admin/index.cgi:101
-msgid "FixMyStreet admin:"
+#: templates/web/default/alert/list.html:47
+msgid "The simplest alert is our geographic one:"
msgstr ""
-#: web-admin/index.cgi:154
-#, perl-format
-msgid "<strong>%d</strong> live problems"
+#: templates/web/default/report/new/all_councils_text.html:18 templates/web/default/report/new/some_councils_text.html:10 templates/web/default/report/new/some_councils_text.html:11
+msgid "The subject and details of the problem will be public, plus your name if you give us permission."
msgstr ""
-#: web-admin/index.cgi:155
-#, perl-format
-msgid "%d live updates"
+#: bin/send-reports:77
+msgid "The user could not locate the problem on a map, but to see the area around the location they entered"
msgstr ""
-#: web-admin/index.cgi:156
-#, perl-format
-msgid "%d confirmed alerts, %d unconfirmed"
+#: perllib/FixMyStreet/App/Controller/Contact.pm:115
+msgid "There were problems with your report. Please see below."
msgstr ""
-#: web-admin/index.cgi:157
-#, perl-format
-msgid "%d questionnaires sent &ndash; %d answered (%s%%)"
+#: perllib/FixMyStreet/App/Controller/Report/Update.pm:202
+msgid "There were problems with your update. Please see below."
msgstr ""
-#: web-admin/index.cgi:158
-#, perl-format
-msgid "%d council contacts &ndash; %d confirmed, %d unconfirmed"
+#: bin/send-reports:175
+msgid "This email has been sent to both councils covering the location of the problem, as the user did not categorise it; please ignore it if you're not the correct council to deal with the issue, or let us know what category of problem this is so we can add it to our system."
msgstr ""
-#: web-admin/index.cgi:163
-msgid "Graph of problem creation by status over time"
+#: bin/send-reports:178
+msgid "This email has been sent to several councils covering the location of the problem, as the category selected is provided for all of them; please ignore it if you're not the correct council to deal with the issue."
msgstr ""
-#: web-admin/index.cgi:166
-msgid "Problem breakdown by state"
+#: templates/web/default/debug_header.html:3
+msgid "This is a developer site; things might break at any time, and the database will be periodically deleted."
msgstr ""
-#: web-admin/index.cgi:171
-msgid "Update breakdown by state"
+#: templates/web/default/reports/council.html:20
+msgid "This is a summary of all reports for one %s."
msgstr ""
-#: web-admin/index.cgi:187
-msgid "Diligency prize league table"
+#: templates/web/default/reports/council.html:22
+msgid "This is a summary of all reports for this %s."
msgstr ""
-#: web-admin/index.cgi:191
-#, perl-format
-msgid "%d edits by %s"
+#: templates/web/default/reports/index.html:4 templates/web/emptyhomes/reports/index.html:4 templates/web/fiksgatami/reports/index.html:4
+msgid "This is a summary of all reports on this site; select a particular council to see the reports sent there."
msgstr ""
-#: web-admin/index.cgi:194
-msgid "No edits have yet been made."
+#: perllib/FixMyStreet/Cobrand/Default.pm:815 perllib/FixMyStreet/Cobrand/EmptyHomes.pm:117 templates/web/default/report/display.html:111
+msgid "This problem has been fixed"
msgstr ""
-#: web-admin/index.cgi:198
-msgid "Councils"
+#: perllib/FixMyStreet/Cobrand/Default.pm:811
+msgid "This problem is old and of unknown status."
msgstr ""
-#: web-admin/index.cgi:212 web-admin/index.cgi:587
-msgid "None"
+#: perllib/FixMyStreet/DB/ResultSet/AlertType.pm:80
+msgid "This report is currently marked as fixed."
msgstr ""
-#: web-admin/index.cgi:224
-#, perl-format
-msgid "%d addresses"
+#: perllib/FixMyStreet/DB/ResultSet/AlertType.pm:81
+msgid "This report is currently marked as open."
msgstr ""
-#: web-admin/index.cgi:230
-msgid "No info at all"
+#: bin/send-reports:70
+msgid "This web page also contains a photo of the problem, provided by the user."
msgstr ""
-#: web-admin/index.cgi:232
-msgid "Currently has 1+ deleted"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:600 templates/web/default/admin/timeline.html:1
+msgid "Timeline"
msgstr ""
-#: web-admin/index.cgi:234
-msgid "Some unconfirmeds"
+#: templates/web/default/admin/search_reports.html:13
+msgid "Title"
msgstr ""
-#: web-admin/index.cgi:236
-msgid "All confirmed"
+#: templates/web/default/alert/index.html:21
+msgid ""
+"To find out what local alerts we have for you, please enter your GB\n"
+"postcode or street name and area:"
msgstr ""
-#: web-admin/index.cgi:266 web-admin/index.cgi:276 web-admin/index.cgi:293
-msgid "*unknown*"
+#: templates/web/default/around/display_location.html:92
+msgid "To report a problem, simply <strong>click on the map</strong> at the correct location."
msgstr ""
-#: web-admin/index.cgi:269 web-admin/index.cgi:297
-msgid "Values updated"
+#: bin/send-reports:76
+msgid "To view a map of the precise location of this issue"
msgstr ""
-#: web-admin/index.cgi:279
-msgid "New category contact added"
+#: templates/web/default/maps/tilma/original.html:10
+msgid "Unable to fetch the map tiles from the tile server."
msgstr ""
-#: web-admin/index.cgi:316 web-admin/index.cgi:399
-#, perl-format
-msgid "Council contacts for %s"
+#: templates/web/default/admin/report_edit.html:22 templates/web/default/admin/update_edit.html:19
+msgid "Unconfirmed"
msgstr ""
-#: web-admin/index.cgi:329
-msgid " List all reported problems"
+#: perllib/FixMyStreet/App/Controller/Rss.pm:166
+msgid "Unknown alert type"
msgstr ""
-#: web-admin/index.cgi:331
-msgid "Text only version"
+#: perllib/FixMyStreet/App/Controller/Report.pm:70 perllib/FixMyStreet/App/Controller/Report/Update.pm:24 perllib/FixMyStreet/App/Controller/Rss.pm:37
+msgid "Unknown problem ID"
msgstr ""
-#: web-admin/index.cgi:337 web-admin/index.cgi:478
-msgid "Category"
+#: templates/web/default/admin/timeline.html:35
+msgid "Update %s created for problem %d; by %s"
msgstr ""
-#: web-admin/index.cgi:337 web-admin/index.cgi:439 web-admin/index.cgi:478
-#: web-admin/index.cgi:657
-msgid "Email"
+#: templates/web/default/contact/index.html:21
+msgid "Update below added anonymously at %s"
msgstr ""
-#: web-admin/index.cgi:337 web-admin/index.cgi:369 web-admin/index.cgi:422
-#: web-admin/index.cgi:439 web-admin/index.cgi:738
-msgid "Confirmed"
+#: templates/web/default/contact/index.html:23
+msgid "Update below added by %s at %s"
msgstr ""
-#: web-admin/index.cgi:337 web-admin/index.cgi:371 web-admin/index.cgi:424
-#: web-admin/index.cgi:439
-msgid "Deleted"
+#: templates/web/default/admin/index.html:29
+msgid "Update breakdown by state"
msgstr ""
-#: web-admin/index.cgi:337
-msgid "Last editor"
+#: db/alert_types.pl:7
+msgid "Update by {{name}}"
msgstr ""
-#: web-admin/index.cgi:337 web-admin/index.cgi:439
-msgid "Note"
+#: templates/web/default/admin/council_contacts.html:46
+msgid "Update statuses"
msgstr ""
-#: web-admin/index.cgi:337 web-admin/index.cgi:439
-msgid "When edited"
+#: templates/web/default/report/display.html:103
+msgid "Update:"
msgstr ""
-#: web-admin/index.cgi:337
-msgid "Confirm"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:572 perllib/FixMyStreet/App/Controller/Admin.pm:735
+msgid "Updated!"
msgstr ""
-#: web-admin/index.cgi:354
-msgid "Update statuses"
+#: templates/web/default/admin/list_updates.html:1 templates/web/default/report/updates.html:4
+msgid "Updates"
msgstr ""
-#: web-admin/index.cgi:359
-msgid "Add new category"
+#: db/alert_types.pl:5 db/alert_types.pl:6
+msgid "Updates on {{title}}"
msgstr ""
-#: web-admin/index.cgi:362 web-admin/index.cgi:416
-msgid "Category: "
+#: templates/web/default/report/display.html:0 templates/web/default/report/display.html:7
+msgid "Updates to this problem, FixMyStreet"
msgstr ""
-#: web-admin/index.cgi:365 web-admin/index.cgi:419
-msgid "Email: "
+#: perllib/FixMyStreet/App/Controller/Admin.pm:286 perllib/FixMyStreet/App/Controller/Admin.pm:316
+msgid "Values updated"
msgstr ""
-#: web-admin/index.cgi:374 web-admin/index.cgi:427
-msgid "Note: "
+#: templates/web/default/admin/report_edit.html:12 templates/web/default/admin/update_edit.html:12
+msgid "View report on site"
msgstr ""
-#: web-admin/index.cgi:381
-msgid "Create category"
+#: templates/web/emptyhomes/tokens/confirm_problem.html:39
+msgid "View your report"
msgstr ""
-#: web-admin/index.cgi:433
-msgid "Save changes"
+#: templates/web/default/around/display_location.html:0 templates/web/default/around/display_location.html:34
+msgid "Viewing a location"
msgstr ""
-#: web-admin/index.cgi:437
-msgid "History"
+#: templates/web/default/report/display.html:0
+msgid "Viewing a problem"
msgstr ""
-#: web-admin/index.cgi:439
-msgid "Editor"
+#: perllib/FixMyStreet/Geocode.pm:155 perllib/FixMyStreet/Geocode.pm:174
+msgid "We do not cover Northern Ireland, I'm afraid, as our licence doesn't include any maps for the region."
msgstr ""
-#: web-admin/index.cgi:443 web-admin/index.cgi:444
-msgid "yes"
+#: templates/web/default/alert/choose.html:6 templates/web/default/around/around_index.html:33
+msgid "We found more than one match for that location. We show up to ten matches, please try a different search if yours is not here."
msgstr ""
-#: web-admin/index.cgi:443 web-admin/index.cgi:444
-msgid "no"
+#: perllib/FixMyStreet/App/Controller/Around.pm:235
+msgid "We had a problem with the supplied co-ordinates - outside the UK?"
msgstr ""
-#: web-admin/index.cgi:471
-msgid "Search:"
+#: templates/web/emptyhomes/tokens/confirm_problem.html:18 templates/web/emptyhomes/tokens/confirm_problem.html:20
+msgid "We may contact you periodically to ask if anything has changed with the property you reported."
msgstr ""
-#: web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "ID"
+#: bin/send-reports:185
+msgid "We realise this problem might be the responsibility of %s; however, we don't currently have any contact details for them. If you know of an appropriate contact address, please do get in touch."
msgstr ""
-#: web-admin/index.cgi:478
-msgid "Title"
+#: templates/web/default/index.html:62
+msgid "We send it to the council on your behalf"
msgstr ""
-#: web-admin/index.cgi:478
-msgid "Council"
+#: templates/web/default/report/new/notes.html:5
+msgid "We will only use your personal information in accordance with our <a href=\"/faq#privacy\">privacy policy.</a>"
msgstr ""
-#: web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "Cobrand"
+#: templates/web/emptyhomes/contact/blurb.html:2
+msgid "We&rsquo;d love to hear what you think about this website. Just fill in the form. Please don&rsquo;t contact us about individual empty homes; use the box accessed from <a href=\"/\">the front page</a>."
msgstr ""
-#: web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "Created"
+#: templates/web/default/contact/blurb.html:8
+msgid "We'd love to hear what you think about this site. Just fill in the form, or send an email to <a href='mailto:%s'>%s</a>:"
msgstr ""
-#: web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "State"
+#: templates/web/default/admin/council_contacts.html:25 templates/web/default/admin/council_edit.html:41
+msgid "When edited"
msgstr ""
-#: web-admin/index.cgi:478
+#: templates/web/default/admin/search_reports.html:22
msgid "When sent"
msgstr ""
-#: web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "*"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:464
+msgid "Whole block of empty flats"
msgstr ""
-#: web-admin/index.cgi:500 web-admin/index.cgi:634
-msgid "Confirmed:"
+#: templates/web/default/questionnaire/index.html:104
+msgid "Would you like to receive another questionnaire in 4 weeks, reminding you to check the status?"
msgstr ""
-#: web-admin/index.cgi:501
-msgid "Fixed:"
+#: templates/web/default/report/new/notes.html:8
+msgid "Writing your message entirely in block capitals makes it hard to read, as does a lack of punctuation."
msgstr ""
-#: web-admin/index.cgi:502
-msgid "Last&nbsp;update:"
+#: templates/web/default/admin/council_contacts.html:32 templates/web/default/admin/council_contacts.html:33 templates/web/default/admin/council_edit.html:5 templates/web/default/admin/list_updates.html:28 templates/web/default/admin/report_edit.html:18 templates/web/default/admin/report_edit.html:37 templates/web/default/admin/search_reports.html:43 templates/web/default/admin/update_edit.html:15 templates/web/default/questionnaire/creator_fixed.html:14 templates/web/default/questionnaire/index.html:107 templates/web/default/questionnaire/index.html:68
+msgid "Yes"
msgstr ""
-#: web-admin/index.cgi:518 web-admin/index.cgi:676
-msgid "Edit"
+#: templates/web/default/contact/index.html:37
+msgid "You are reporting the following problem report for being abusive, containing personal information, or similar:"
msgstr ""
-#: web-admin/index.cgi:543
-msgid "That problem will now be resent."
+#: templates/web/default/contact/index.html:15
+msgid "You are reporting the following update for being abusive, containing personal information, or similar:"
msgstr ""
-#: web-admin/index.cgi:549
-msgid "I am afraid you cannot confirm unconfirmed reports."
+#: templates/web/default/reports/council.html:35
+msgid "You can <a href=\"%s\">see less detail</a> or go back and <a href=\"/reports\">show all councils</a>."
msgstr ""
-#: web-admin/index.cgi:578 web-admin/index.cgi:698
-msgid "Updated!"
+#: templates/web/default/reports/council.html:31
+msgid "You can <a href=\"%s\">see less detail</a>."
msgstr ""
-#: web-admin/index.cgi:582
-#, perl-format
-msgid "Editing problem %d"
+#: templates/web/default/reports/council.html:37
+msgid "You can <a href=\"%s\">see more details</a> or go back and <a href=\"/reports\">show all councils</a>."
msgstr ""
-#: web-admin/index.cgi:592
-msgid "used map"
+#: templates/web/default/reports/council.html:33
+msgid "You can <a href=\"%s\">see more details</a>."
msgstr ""
-#: web-admin/index.cgi:592
-msgid "didn't use map"
+#: templates/web/default/report/new/no_councils_text.html:14 templates/web/default/report/new/no_councils_text.html:3 templates/web/default/report/new/some_councils_text.html:20 templates/web/default/report/new/some_councils_text.html:22
+msgid "You can help us by finding a contact email address for local problems for %s and emailing it to us at <a href='mailto:%s'>%s</a>."
msgstr ""
-#: web-admin/index.cgi:604 web-admin/index.cgi:734
-msgid "Remove photo (can't be undone!)"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:42
+msgid "You have already answered this questionnaire. If you have a question, please <a href='%s'>get in touch</a>, or <a href='%s'>view your problem</a>.\n"
msgstr ""
-#: web-admin/index.cgi:609
-msgid "Anonymous:"
+#: templates/web/default/questionnaire/index.html:94 templates/web/default/report/display.html:122 templates/web/default/report/new/fill_in_details.html:111
+msgid "You have already attached a photo to this report, attaching another one will replace it."
msgstr ""
-#: web-admin/index.cgi:610 web-admin/index.cgi:738
-msgid "State:"
+#: templates/web/default/auth/logout.html:3
+msgid "You have been logged out"
msgstr ""
-#: web-admin/index.cgi:610
-msgid "Open"
+#: templates/web/default/report/new/fill_in_details.html:36
+msgid "You have located the problem at the point marked with a purple pin on the map. If this is not the correct location, simply click on the map again. "
msgstr ""
-#: web-admin/index.cgi:610
-msgid "Fixed"
+#: templates/web/default/tokens/confirm_alert.html:7
+msgid "You have successfully confirmed your alert."
msgstr ""
-#: web-admin/index.cgi:610 web-admin/index.cgi:738
-msgid "Hidden"
+#: templates/web/default/tokens/confirm_problem.html:6 templates/web/default/tokens/confirm_problem.html:7
+msgid "You have successfully confirmed your problem"
msgstr ""
-#: web-admin/index.cgi:610 web-admin/index.cgi:738
-msgid "Unconfirmed"
+#: templates/web/default/tokens/confirm_update.html:11 templates/web/default/tokens/confirm_update.html:12
+msgid "You have successfully confirmed your update and you can now <a href=\"%s\">view it on the site</a>."
msgstr ""
-#: web-admin/index.cgi:610
-msgid "Partial"
+#: templates/web/default/tokens/confirm_alert.html:11
+msgid "You have successfully created your alert."
msgstr ""
-#: web-admin/index.cgi:613
-msgid "You really want to resend?"
+#: templates/web/default/tokens/confirm_alert.html:9
+msgid "You have successfully deleted your alert."
msgstr ""
-#: web-admin/index.cgi:613
-msgid "Resend report"
+#: templates/web/default/email_sent.html:30
+msgid "You must now click the link in the email we've just sent you &mdash; if you do not, %s."
msgstr ""
-#: web-admin/index.cgi:622
-msgid "View report on site"
+#: templates/web/default/admin/report_edit.html:32
+msgid "You really want to resend?"
msgstr ""
-#: web-admin/index.cgi:625
-msgid "Co-ordinates:"
+#: templates/web/default/my/my.html:3
+msgid "Your Reports"
msgstr ""
-#: web-admin/index.cgi:625
-msgid "originally entered"
+#: templates/web/default/alert/list.html:123
+msgid "Your email:"
msgstr ""
-#: web-admin/index.cgi:626
-msgid "For council(s):"
+#: templates/web/default/contact/index.html:68
+msgid "Your name:"
msgstr ""
-#: web-admin/index.cgi:626
-msgid "other areas:"
+#: templates/web/default/contact/index.html:76
+msgid "Your&nbsp;email:"
msgstr ""
-#: web-admin/index.cgi:633 web-admin/index.cgi:754
-msgid "Created:"
+#: templates/web/default/admin/timeline.html:6
+msgid "by %s"
msgstr ""
-#: web-admin/index.cgi:635
-msgid "Sent:"
+#: templates/web/default/reports/council.html:6 templates/web/default/reports/council.html:7
+msgid "council"
msgstr ""
-#: web-admin/index.cgi:636
-msgid "Last update:"
+#: templates/web/default/admin/report_edit.html:15
+msgid "didn't use map"
msgstr ""
-#: web-admin/index.cgi:637
-msgid "Service:"
+#: perllib/Utils.pm:241
+msgid "less than a minute"
msgstr ""
-#: web-admin/index.cgi:638 web-admin/index.cgi:752
-msgid "Cobrand:"
+#: templates/web/default/report/updates.html:13
+msgid "marked as fixed"
msgstr ""
-#: web-admin/index.cgi:639 web-admin/index.cgi:753
-msgid "Cobrand data:"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:107 templates/web/default/admin/questionnaire.html:15 templates/web/default/admin/questionnaire.html:16
+msgid "n/a"
msgstr ""
-#: web-admin/index.cgi:640
-msgid "Going to send questionnaire?"
+#: templates/web/default/alert/list.html:119
+msgid "or"
msgstr ""
-#: web-admin/index.cgi:644 web-admin/index.cgi:758
-msgid "Submit changes"
+#: templates/web/default/admin/report_edit.html:15
+msgid "originally entered"
msgstr ""
-#: web-admin/index.cgi:657
-msgid "Text"
+#: templates/web/default/admin/report_edit.html:16
+msgid "other areas:"
msgstr ""
-#: web-admin/index.cgi:703
-msgid "Problem marked as open."
+#: templates/web/default/report/updates.html:14
+msgid "reopened"
msgstr ""
-#: web-admin/index.cgi:719
-#, perl-format
-msgid "Editing update %d"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:361
+msgid "the map was not used so pin location may be inaccurate"
msgstr ""
-#: web-admin/index.cgi:747
-msgid "View update on site"
+#: bin/send-reports:166
+msgid "this type of local problem"
msgstr ""
-#: web-admin/index.cgi:748
-msgid "Text:"
+#: perllib/Utils.pm:217
+msgid "today"
msgstr ""
-#: web-admin/index.cgi:750
-msgid "(blank to go anonymous)"
+#: templates/web/default/admin/report_edit.html:15
+msgid "used map"
msgstr ""
-#: web-admin/index.cgi:832 web-admin/index.cgi:835
-#, perl-format
-msgid "by %s"
+#: templates/web/default/reports/council.html:0 templates/web/default/reports/council.html:3
+msgid "ward"
msgstr ""
-#: web-admin/index.cgi:833
-#, perl-format
-msgid "Problem %d created"
+#: templates/web/default/email_sent.html:15 templates/web/default/email_sent.html:3
+msgid "we'll hang on to your alert while you're checking your email."
msgstr ""
-#: web-admin/index.cgi:838
-#, perl-format
-msgid "Problem %s confirmed"
+#: templates/web/default/email_sent.html:3 templates/web/default/email_sent.html:7
+msgid "we'll hang on to your problem report while you're checking your email."
msgstr ""
-#: web-admin/index.cgi:842
-#, perl-format
-msgid "Problem %s sent to council %s"
+#: templates/web/default/email_sent.html:11 templates/web/default/email_sent.html:3
+msgid "we'll hang on to your update while you're checking your email."
msgstr ""
-#: web-admin/index.cgi:844
-#, perl-format
-msgid "Questionnaire %d sent for problem %d"
+#: templates/web/default/email_sent.html:14 templates/web/default/email_sent.html:3
+msgid "your alert will not be activated"
msgstr ""
-#: web-admin/index.cgi:846
-#, perl-format
-msgid "Questionnaire %d answered for problem %d, %s to %s"
+#: templates/web/default/email_sent.html:3 templates/web/default/email_sent.html:6
+msgid "your problem will not be posted"
msgstr ""
-#: web-admin/index.cgi:851
-#, perl-format
-msgid "Update %s created for problem %d; by %s"
+#: templates/web/default/email_sent.html:10 templates/web/default/email_sent.html:3
+msgid "your update will not be posted"
msgstr ""
-#: web-admin/index.cgi:855
+#: templates/web/default/front_stats.html:19
#, perl-format
-msgid "Alert %d created for %s, type %s, parameters %s / %s"
-msgstr ""
+msgid "<big>%s</big> report recently"
+msgid_plural "<big>%s</big> reports recently"
+msgstr[0] ""
+msgstr[1] ""
-#: web-admin/index.cgi:858
+#: templates/web/default/report/new/no_councils_text.html:5
#, perl-format
-msgid "Alert %d disabled (created %s)"
-msgstr ""
+msgid "We do not yet have details for the council that covers this location."
+msgid_plural "We do not yet have details for the councils that cover this location."
+msgstr[0] ""
+msgstr[1] ""
-#: web-admin/index.cgi:890
-msgid "Not reported before"
-msgstr ""
+#: templates/web/default/front_stats.html:14
+#, perl-format
+msgid "<big>%s</big> report in past week"
+msgid_plural "<big>%s</big> reports in past week"
+msgstr[0] ""
+msgstr[1] ""
-#. Please leave the first word "Subject:" untranslated
+#: templates/web/default/front_stats.html:25
+#, perl-format
+msgid "<big>%s</big> fixed in past month"
+msgid_plural "<big>%s</big> fixed in past month"
+msgstr[0] ""
+msgstr[1] ""
-#: templates/emails/alert-confirm
-msgid ""
-"Subject: Confirm your alert on FixMyStreet\n"
-"\n"
-"Hi,\n"
-"\n"
-"Please click on the link below to confirm the alert you just\n"
-"asked to subscribe to on FixMyStreet:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"If you can't click the link, please copy and paste it to the\n"
-"address bar of your web browser.\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-msgstr ""
-
-#: templates/emails/alert-problem
-msgid ""
-"Subject: New problems on FixMyStreet\n"
-"\n"
-"The following new problems have been added:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"To stop receiving emails when there are new problems,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/alert-problem-area
-msgid ""
-"Subject: New problems in <?=$values['area_name']?> on FixMyStreet\n"
-"\n"
-"The following new problems have been added within\n"
-"<?=$values['area_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"To stop receiving emails when there are new problems in\n"
-"<?=$values['area_name']?>, please follow this link: \n"
-"<?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/alert-problem-council
-msgid ""
-"Subject: New problems reported to <?=$values['area_name']?> on FixMyStreet\n"
-"\n"
-"The following new problems have been reported to <?=$values['area_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"To stop receiving emails when there are new problems reported to\n"
-"<?=$values['area_name']?>, please follow this link: \n"
-"<?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/alert-problem-nearby
-msgid ""
-"Subject: New nearby problems on FixMyStreet\n"
-"\n"
-"The following nearby problems have been added:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"To stop receiving emails when there are nearby problems,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/alert-problem-ward
-msgid ""
-"Subject: New problems reported to <?=$values['area_name']?> within <?=$values['ward_name']?> on FixMyStreet\n"
-"\n"
-"The following new problems have been reported to <?=$values['area_name']?>\n"
-"within <?=$values['ward_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"To stop receiving emails when there are new problems reported to\n"
-"<?=$values['area_name']?> within <?=$values['ward_name']?>,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/alert-update
-msgid ""
-"Subject: New updates on problem - '<?=$values['title']?>'\n"
-"\n"
-"The following updates have been left on this problem:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"<?=$values['state_message']?>\n"
-"\n"
-"To view or reply to these updates, please visit the following URL:\n"
-" <?=$values['problem_url']?>\n"
-"\n"
-"You cannot contact anyone by replying to this email.\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"To stop receiving emails when there are new updates on this problem,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/empty property-confirm
-msgid ""
-"Subject: Confirm your empty property report\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"Please click on the link below to confirm the empty property\n"
-"report you just added to the site:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"If your email program does not let you click on this link,\n"
-"copy and paste it into your web browser and press return.\n"
-"\n"
-"Your report had the subject:\n"
-"<?=$values['title']?>\n"
-"\n"
-"And details:\n"
-"<?=$values['detail']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-msgstr ""
-
-#: templates/emails/flickr-confirm
-msgid ""
-"Subject: Confirm your email address on FixMyStreet\n"
-"\n"
-"Hi,\n"
-"\n"
-"Please click on the link below to confirm the email address\n"
-"you just gave to FixMyStreet:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"This is so we can look up the photos you tag with FixMyStreet,\n"
-"and send you an email letting you know about your new problems.\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-msgstr ""
-
-#: templates/emails/flickr-submit
-msgid ""
-"Subject: New photo pulled from Flickr to FixMyStreet\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"We've fetched a photo you uploaded to Flickr and tagged with\n"
-"FixMyStreet. To check the details we have, and to add any more,\n"
-"please visit the following URL:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"Then we can send your photo to the council. Thanks!\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-msgstr ""
-
-#: templates/emails/partial
-msgid ""
-"Subject: Confirm your report on FixMyStreet\n"
-"\n"
-"Hi<?=$values['name']?>,\n"
-"\n"
-"To confirm the report you have uploaded to FixMyStreet via\n"
-"<?=$values['service']?>, and to check or add any details,\n"
-"please visit the following URL:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"Thanks!\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-msgstr ""
-
-#: templates/emails/problem-confirm
-msgid ""
-"Subject: Confirm your problem on FixMyStreet\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"Please click on the link below to confirm the problem you just\n"
-"added to FixMyStreet:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"If your email program does not let you click on this link,\n"
-"copy and paste it into your web browser and press return.\n"
-"\n"
-"Your problem had the title:\n"
-"<?=$values['title']?>\n"
-"\n"
-"And details:\n"
-"<?=$values['detail']?>\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-msgstr ""
-
-#: templates/emails/questionnaire
-msgid ""
-"Subject: Questionnaire about your problem on FixMyStreet\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"<?=$values['created']?> ago, you left a problem on FixMyStreet\n"
-"with the details provided at the end of this email. To keep our\n"
-"site up to date and relevant, we'd appreciate it if you could\n"
-"follow the link below and fill in our short questionnaire\n"
-"updating the status of your problem:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"Please do not reply to this email; there is a public comment\n"
-"box on the questionnaire.\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"Your problem was as follows:\n"
-"\n"
-"<?=$values['title']?>\n"
-"\n"
-"<?=$values['detail']?>\n"
-"\n"
-msgstr ""
-
-#: templates/emails/questionnaire-eha-26weeks
-msgid ""
-"Subject: Questionnaire about your empty property report\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"Six months ago, you reported an empty home on ReportEmptyHomes.com with the\n"
-"details provided at the end of this email. To keep our site up to date and\n"
-"relevant, I'd be grateful if you could fill in this short questionnaire to tell\n"
-"us what has happened: \n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"Please do not reply to this email; there is a public comment\n"
-"box on the questionnaire.\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"Your report was as follows:\n"
-"\n"
-"<?=$values['title']?>\n"
-"\n"
-"Property type: <?=$values['category']?>\n"
-"\n"
-"<?=$values['detail']?>\n"
-"\n"
-msgstr ""
-
-#: templates/emails/questionnaire-eha-4weeks
-msgid ""
-"Subject: Questionnaire about your empty property report\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"Four weeks ago, you reported an empty home on ReportEmptyHomes.com with the\n"
-"details provided at the end of this email. To keep our site up to date and\n"
-"relevant, I'd be grateful if you could fill in this short questionnaire to tell\n"
-"us what has happened:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"Please do not reply to this email; there is a public comment\n"
-"box on the questionnaire.\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"Your report was as follows:\n"
-"\n"
-"<?=$values['title']?>\n"
-"\n"
-"Property type: <?=$values['category']?>\n"
-"\n"
-"<?=$values['detail']?>\n"
-"\n"
-msgstr ""
-
-#: templates/emails/reply-autoresponse
-msgid ""
-"Subject: Automatic reply to your message to FixMyStreet\n"
-"\n"
-"Hi,\n"
-"\n"
-"This is an automatic response to your email; your email has not been delivered.\n"
-"\n"
-"If you're replying to an email about a report update, please visit\n"
-"the URL given in the email in order to leave a reply. You cannot\n"
-"reply to an update via email.\n"
-"\n"
-"If you are trying to confirm something, such as a report or an email\n"
-"alert, please click the link in the email that we sent you, or, if\n"
-"you cannot click the link, copy and paste it into the address bar of\n"
-"your web browser.\n"
-"\n"
-"If you're trying to unsubscribe from an email alert, there is an\n"
-"unsubscribe link at the bottom of the email.\n"
-"\n"
-"If you have a question or comment about the site, please send your\n"
-"email to team@fixmystreet.com\n"
-"\n"
-"Yours,\n"
-"The FixMyStreet team\n"
-msgstr ""
-
-#: templates/emails/submit-brent
-msgid ""
-"Subject: FMS Problem Report: <?=$values['title']?>\n"
-"\n"
-"Dear <?=$values['councils_name']?>,\n"
-"\n"
-"<?=$values['missing']?><?=$values['multiple']?>A user of\n"
-"FixMyStreet has submitted the following report\n"
-"of a local problem that they believe might require your attention.\n"
-"\n"
-"<?=$values['fuzzy']?>, or to provide an update on the problem,\n"
-"please visit the following link:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"<?=$values['has_photo']?>----------\n"
-"\n"
-"Name: <?=$values['name']?>\n"
-"\n"
-"Email: <?=$values['email']?>\n"
-"\n"
-"<?=$values['phone_line']?><?=$values['category_line']?>Subject: <?=$values['title']?>\n"
-"\n"
-"Details: <?=$values['detail']?>\n"
-"\n"
-"<?=$values['easting_northing']?>Latitude: <?=$values['latitude']?>\n"
-"\n"
-"Longitude: <?=$values['longitude']?>\n"
-"\n"
-"<?=$values['closest_address_machine']?>----------\n"
-"\n"
-"Replies to this email will go to the user who submitted the problem.\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"[ This message was sent via FixMyStreet, a project of UKCOD, registered charity\n"
-"number 1076346. If there is a more appropriate email address for messages about\n"
-"<?=$values['category_footer']?>, please let us know by visiting <http://www.fixmystreet.com/contact>.\n"
-"This will help improve the service for local people. We\n"
-"also welcome any other feedback you may have. ]\n"
-"\n"
-msgstr ""
-
-#: templates/emails/submit-council
-msgid ""
-"Subject: Problem Report: <?=$values['title']?>\n"
-"\n"
-"Dear <?=$values['councils_name']?>,\n"
-"\n"
-"<?=$values['missing']?><?=$values['multiple']?>A user of\n"
-"FixMyStreet has submitted the following report\n"
-"of a local problem that they believe might require your attention.\n"
-"\n"
-"<?=$values['fuzzy']?>, or to provide an update on the problem,\n"
-"please visit the following link:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"<?=$values['has_photo']?>----------\n"
-"\n"
-"Name: <?=$values['name']?>\n"
-"\n"
-"Email: <?=$values['email']?>\n"
-"\n"
-"<?=$values['phone_line']?><?=$values['category_line']?>Subject: <?=$values['title']?>\n"
-"\n"
-"Details: <?=$values['detail']?>\n"
-"\n"
-"<?=$values['easting_northing']?>Latitude: <?=$values['latitude']?>\n"
-"\n"
-"Longitude: <?=$values['longitude']?>\n"
-"\n"
-"<?=$values['closest_address']?>----------\n"
-"\n"
-"Replies to this email will go to the user who submitted the problem.\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"[ This message was sent via FixMyStreet, a project of UKCOD, registered charity\n"
-"number 1076346. If there is a more appropriate email address for messages about\n"
-"<?=$values['category_footer']?>, please let us know by visiting <http://www.fixmystreet.com/contact>.\n"
-"This will help improve the service for local people. We\n"
-"also welcome any other feedback you may have. ]\n"
-"\n"
-msgstr ""
-
-#: templates/emails/submit-eha
-msgid ""
-"Subject: Empty property report\n"
-"\n"
-"Dear Empty Property Officer,\n"
-"\n"
-"This is a new referral of an empty property in your area made by a user of the\n"
-"website ReportEmptyHomes.com; the website user has been told that the case has\n"
-"been referred to you. We would be grateful if you could do whatever you can to\n"
-"help get this property back into use. We will contact the user in a month and\n"
-"again in six months and ask them what has happened to the property. \n"
-"\n"
-"We'd encourage you to tell us what you have done, and when the property comes\n"
-"back into use, by filling in an update against the property referral on the\n"
-"website:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"This gives useful feedback to website users and helps them understand what\n"
-"action you are taking. We will offer advice to the user on other action they\n"
-"might take if the property isn't successfully dealt with. \n"
-"\n"
-"<?=$values['has_photo']?>If you would like help or advice on getting empty properties back into use\n"
-"there is lots of useful information on the Empty Homes Agency's website\n"
-"www.EmptyHomes.com - if you have further questions please give us a call. \n"
-"\n"
-"----------\n"
-"\n"
-"Name: <?=$values['name']?>\n"
-"\n"
-"Email: <?=$values['email']?>\n"
-"\n"
-"<?=$values['phone_line']?>Subject: <?=$values['title']?>\n"
-"\n"
-"Property type: <?=$values['category']?>\n"
-"\n"
-"Details: <?=$values['detail']?>\n"
-"\n"
-"<?=$values['closest_address']?>----------\n"
-"\n"
-"Replies to this email will go to the user who submitted the report, if\n"
-"you would like to ask for any further information.\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-msgstr ""
-
-#: templates/emails/tms-confirm
-msgid ""
-"Subject: Confirm your expression of interest in TextMyStreet\n"
-"\n"
-"Hi,\n"
-"\n"
-"Please click on the link below to confirm your expression of\n"
-"interest in TextMyStreet:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"If you can't click the link, please copy and paste it to the\n"
-"address bar of your web browser.\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-msgstr ""
-
-#: templates/emails/update-confirm
-msgid ""
-"Subject: Confirm your update on FixMyStreet\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"Please click on the link below to confirm the update you just wrote:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"If you can't click the link, please copy and paste it to the\n"
-"address bar of your web browser.\n"
-"\n"
-"Your update reads:\n"
-"\n"
-"<?=$values['update']?>\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-msgstr ""
-
-#: templates/emails/emptyhomes/alert-problem
-msgid ""
-"Subject: New empty properties on reportemptyhomes.com\n"
-"\n"
-"The following empty properties have been added:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are new properties,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/emptyhomes/alert-problem-area
-msgid ""
-"Subject: New empty properties in <?=$values['area_name']?> on reportemptyhomes.com\n"
-"\n"
-"The following empty properties have been added within\n"
-"<?=$values['area_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are new properties in\n"
-"<?=$values['area_name']?>, please follow this link: \n"
-"<?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/emptyhomes/alert-problem-council
-msgid ""
-"Subject: New empty properties reported to <?=$values['area_name']?> on reportemptyhomes.com\n"
-"\n"
-"The following empty properties have been reported to <?=$values['area_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are new properties reported to\n"
-"<?=$values['area_name']?>, please follow this link: \n"
-"<?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/emptyhomes/alert-problem-nearby
-msgid ""
-"Subject: New nearby empty properties on reportemptyhomes.com\n"
-"\n"
-"The following nearby empty properties have been added:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are nearby properties,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/emptyhomes/alert-problem-ward
-msgid ""
-"Subject: New empty properties reported to <?=$values['area_name']?> within <?=$values['ward_name']?> on reportemptyhomes.com\n"
-"\n"
-"The following empty properties have been reported to <?=$values['area_name']?>\n"
-"within <?=$values['ward_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are new properties reported to\n"
-"<?=$values['area_name']?> within <?=$values['ward_name']?>,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/emptyhomes/alert-update
-msgid ""
-"Subject: New updates on empty property- '<?=$values['title']?>'\n"
-"\n"
-"The following updates have been left on this empty property:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"View or reply to these updates: <?=$values['problem_url']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are new updates on this property,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
+#: templates/web/default/report/new/some_councils_text.html:14
+#, perl-format
+msgid "We do <strong>not</strong> yet have details for the other council that covers this location."
+msgid_plural "We do <strong>not</strong> yet have details for the other councils that cover this location."
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/web/default/front_stats.html:31
+#, perl-format
+msgid "<big>%s</big> update on reports"
+msgid_plural "<big>%s</big> updates on reports"
+msgstr[0] ""
+msgstr[1] ""
diff --git a/locale/cy_GB.UTF-8/LC_MESSAGES/EmptyHomes.po b/locale/cy_GB.UTF-8/LC_MESSAGES/EmptyHomes.po
index 867352f70..e483e0c9b 100644
--- a/locale/cy_GB.UTF-8/LC_MESSAGES/EmptyHomes.po
+++ b/locale/cy_GB.UTF-8/LC_MESSAGES/EmptyHomes.po
@@ -10,7 +10,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 1.0\n"
"Report-Msgid-Bugs-To: matthew@mysociety.org\n"
-"POT-Creation-Date: 2011-05-04 19:21+0100\n"
+"POT-Creation-Date: 2011-06-09 18:49+0100\n"
"PO-Revision-Date: 2009-07-10 14:20-0000\n"
"Last-Translator: Mark Smith <Mark.Smith@trosol.co.uk>\n"
"Language-Team: mySociety\n"
@@ -19,100 +19,188 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-#: perllib/Cobrands/Emptyhomes/Util.pm:61 bin/send-questionnaires-eha:93
-msgid "Report Empty Homes"
-msgstr "Adrodd am Eiddo Gwag"
+#: bin/send-reports:173
+msgid " and "
+msgstr ""
-#: perllib/Cobrands/Fiksgatami/Util.pm:37
+#: templates/web/default/tokens/confirm_problem.html:10
+#: templates/web/default/tokens/confirm_problem.html:6
+msgid " and <strong>we will now send it to the council</strong>"
+msgstr " a <strong>byddwn nawr yn ei anfon at y cyngor</strong>"
+
+#: templates/web/default/report/new/all_councils_text.html:10
+#: templates/web/default/report/new/all_councils_text.html:3
+#: templates/web/default/report/new/no_councils_text.html:15
+#: templates/web/default/report/new/no_councils_text.html:3
+#: templates/web/default/report/new/some_councils_text.html:20
+#: templates/web/default/report/new/some_councils_text.html:23
+#: templates/web/default/report/new/some_councils_text.html:5
+#: templates/web/emptyhomes/report/new/all_councils_text.html:2
#, fuzzy
-msgid "Enter a nearby postcode, or street name and area"
-msgstr "Cofnodwch god post Prydeinig, neu enw stryd ac ardal gerllaw:"
+msgid " or "
+msgstr "neu"
-#: perllib/Page.pm:86
-#, perl-format
-msgid ""
-"Please try again later, or <a href=\"mailto:%s\">email us</a> to let us know."
+#: templates/web/default/admin/council_list.html:17
+#, fuzzy
+msgid "%d addresses"
+msgstr "wythnos"
+
+#: templates/web/default/admin/index.html:16
+msgid "%d confirmed alerts, %d unconfirmed"
msgstr ""
-"Rhowch gynnig eto'n ddiweddarach, neu <a href=\"mailto:%s\">anfonwch neges e-"
-"bost atom</a>i roi gwybod inni."
-#: perllib/Page.pm:87
-msgid "Sorry! Something's gone wrong."
-msgstr "Ymddiheuriadau! Mae rhywbeth wedi mynd o'i le."
+#: templates/web/default/admin/index.html:18
+msgid "%d council contacts &ndash; %d confirmed, %d unconfirmed"
+msgstr ""
-#: perllib/Page.pm:88
-msgid "The text of the error was:"
-msgstr "Testun y gwall oedd:"
+#: perllib/Utils.pm:245
+#, fuzzy
+msgid "%d day"
+msgstr "diwrnod"
-#: perllib/Page.pm:197 web/contact.cgi:107
-msgid "reportemptyhomes.com"
-msgstr "reportemptyhomes.com"
+#: perllib/Utils.pm:245
+#, fuzzy
+msgid "%d days"
+msgstr "diwrnod"
-#: perllib/Page.pm:200 perllib/Page.pm:324
-msgid "Report a property"
-msgstr "Rhoi gwybod am eiddo"
+#: templates/web/default/admin/council_list.html:27
+msgid "%d edits by %s"
+msgstr ""
-#: perllib/Page.pm:201 perllib/Page.pm:325
-msgid "Reports"
-msgstr "Adroddiadau"
+#: perllib/Utils.pm:246
+#, fuzzy
+msgid "%d hour"
+msgstr "awr"
-#: perllib/Page.pm:202 perllib/Page.pm:326
-msgid "Get local reports"
-msgstr "Gweld adroddiadau lleol"
+#: perllib/Utils.pm:246
+#, fuzzy
+msgid "%d hours"
+msgstr "awr"
-#: perllib/Page.pm:203 perllib/Page.pm:327
-msgid "FAQs"
-msgstr "Cwestiynau Cyffredin"
+#: templates/web/default/admin/index.html:15
+#, fuzzy
+msgid "%d live updates"
+msgstr "Anfonwch ddiweddariadau ataf i drwy'r e-bost"
-#: perllib/Page.pm:204 web/about.cgi:19 web/about.cgi:21
-msgid "About us"
-msgstr "Amdanom ni"
+#: perllib/Utils.pm:247
+#, fuzzy
+msgid "%d minute"
+msgstr "munud"
-#: perllib/Page.pm:225
-msgid "Fix<span id=\"my\">My</span>Street"
-msgstr "Trwsio<span id=\"my\">Fy</span>Stryd"
+#: perllib/Utils.pm:247
+#, fuzzy
+msgid "%d minutes"
+msgstr "munud"
-#: perllib/Page.pm:303
+#: templates/web/default/admin/index.html:17
+msgid "%d questionnaires sent &ndash; %d answered (%s%%)"
+msgstr ""
+
+#: perllib/Utils.pm:244
#, fuzzy
-msgid ""
-"This is a developer site; things might break at any time, and the database "
-"will be periodically deleted."
-msgstr "Safle datblygwr yw hwn; gallai pethau dorri unrhyw bryd."
+msgid "%d week"
+msgstr "wythnos"
-#: perllib/Page.pm:317
-msgid ""
-"Built by <a href=\"http://www.mysociety.org/\">mySociety</a>, using some <a "
-"href=\"http://github.com/mysociety/fixmystreet\">clever</a>&nbsp;<a href="
-"\"https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa"
-"\">code</a>."
+#: perllib/Utils.pm:244
+#, fuzzy
+msgid "%d weeks"
+msgstr "wythnos"
+
+#: templates/web/default/reports/council.html:11
+#: templates/web/default/reports/council.html:12
+msgid "%s - Summary reports"
+msgstr "%s - Adroddiadau cryno"
+
+#: perllib/FixMyStreet/Cobrand/Default.pm:738
+#: perllib/FixMyStreet/Cobrand/Default.pm:752
+msgid "%s ward, %s"
msgstr ""
-"Adeiladwyd gan <a href=\"http://www.mysociety.org/\">mySociety</a>, gan "
-"ddefnyddio tipyn o <a href=\"https://secure.mysociety.org/cvstrac/dir?"
-"d=mysociety/services/TilMa\">god</a><a href=\"http://github.com/mysociety/"
-"fixmystreet\">clyfar</a>&nbsp;."
-#: perllib/Page.pm:319
+#: perllib/FixMyStreet/DB/Result/Problem.pm:307
+msgid "%s, reported anonymously at %s"
+msgstr "Rhoddodd %s wybod yn ddi-enw am %s"
+
+#: perllib/FixMyStreet/DB/Result/Problem.pm:309
+msgid "%s, reported by %s at %s"
+msgstr "Adroddwyd am %s gan %s am %s"
+
+#: perllib/FixMyStreet/Cobrand/Default.pm:769
+#: perllib/FixMyStreet/Cobrand/Default.pm:783
+#, fuzzy
+msgid "%s, within %s ward"
+msgstr "Eiddo gwag yn ward %s"
+
+#: perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm:21
+#: perllib/FixMyStreet/Map/Tilma/Original/1_10k.pm:21
#, fuzzy
msgid ""
-"Built by <a href=\"http://www.mysociety.org/\">mySociety</a> and maintained "
-"by <a href=\"http://www.nuug.no/\">NUUG</a>, using some <a href=\"http://"
-"github.com/mysociety/fixmystreet\">clever</a>&nbsp;<a href=\"https://secure."
-"mysociety.org/cvstrac/dir?d=mysociety/services/TilMa\">code</a>."
+"&copy; Crown copyright. All rights reserved. Ministry of Justice "
+"100037819&nbsp;2008."
+msgstr "Hawlfraint y Goron. Cedwir pob hawl. Y Weinyddiaeth Amddiffyn"
+
+#: templates/web/default/email_sent.html:32
+msgid "(Don't worry &mdash; %s)"
msgstr ""
-"Adeiladwyd gan <a href=\"http://www.mysociety.org/\">mySociety</a>, gan "
-"ddefnyddio tipyn o <a href=\"https://secure.mysociety.org/cvstrac/dir?"
-"d=mysociety/services/TilMa\">god</a><a href=\"http://github.com/mysociety/"
-"fixmystreet\">clyfar</a>&nbsp;."
-#: perllib/Page.pm:323
-msgid "Navigation"
-msgstr "Mordwyo "
+#: templates/web/default/alert/list.html:53
+msgid "(a default distance which covers roughly 200,000 people)"
+msgstr "(pellter rhagosodedig sy'n cynnwys tua 200,000 o bobl)"
-#: perllib/Page.pm:328
-msgid "Contact"
-msgstr "Cysylltu"
+#: templates/web/default/alert/list.html:58
+msgid "(alternatively the RSS feed can be customised, within"
+msgstr "(fel arall, gellir addasu'r porthiant RSS yn bersonol, o fewn"
+
+#: templates/web/default/around/around_map_list_items.html:10
+#: templates/web/default/around/on_map_list_items.html:7
+msgid "(returned to use)"
+msgstr "(wedi'i adfer i'w ddefnyddio)"
+
+#: templates/web/default/index.html:4 templates/web/default/index.html:8
+msgid " "
+msgstr " "
+
+#: templates/web/default/reports/council.html:90
+#, fuzzy
+msgid "(not sent to council)"
+msgstr "Ni roddwyd gwybod i'r cyngor am hyn"
+
+#: templates/web/default/report/display.html:82
+#: templates/web/default/report/new/fill_in_details.html:160
+msgid "(optional)"
+msgstr "(dewisol)"
-#: perllib/Page.pm:330
+#: templates/web/default/reports/council.html:88
+#, fuzzy
+msgid "(sent to both)"
+msgstr "Ni roddwyd gwybod i'r cyngor am hyn"
+
+#: templates/web/default/report/display.html:88
+#: templates/web/default/report/new/fill_in_details.html:145
+msgid "(we never show your email address or phone number)"
+msgstr "(nid ydym byth yn dangos eich cyfeiriad e-bost na'ch rhif ffôn)"
+
+#: perllib/FixMyStreet/App/Controller/Admin.pm:261
+msgid "*unknown*"
+msgstr ""
+
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:475
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:501
+#: perllib/FixMyStreet/DB/Result/Problem.pm:204
+#, fuzzy
+msgid "-- Pick a category --"
+msgstr "-- Dewiswch fath o eiddo --"
+
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:461
+#: perllib/FixMyStreet/DB/Result/Problem.pm:210
+msgid "-- Pick a property type --"
+msgstr "-- Dewiswch fath o eiddo --"
+
+#: templates/web/default/tokens/confirm_problem.html:14
+#: templates/web/default/tokens/confirm_problem.html:6
+msgid ". You can <a href=\"%s\">view the empty property on this site</a>."
+msgstr ". Gallwch <a href=\"%s\">weld yr eiddo gwag ar y safle hwn</a>."
+
+#: templates/web/default/footer.html:13
msgid ""
"<a href=\"http://www.mysociety.org/\"><img id=\"logo\" width=\"133\" height="
"\"26\" src=\"/i/mysociety-dark.png\" alt=\"View mySociety.org\"><span id="
@@ -122,461 +210,565 @@ msgstr ""
"\"26\" src=\"/i/mysociety-dark.png\" alt=\"View mySociety.org\"><span id="
"\"logoie\"></span></a>"
-#: perllib/Page.pm:388
-msgid "Error"
-msgstr "Gwall"
+#: templates/web/default/questionnaire/completed.html:21
+#, fuzzy
+msgid ""
+"<p style=\"font-size:150%\">Thank you very much for filling in our "
+"questionnaire; glad to hear it&rsquo;s been returned to use.</p>"
+msgstr ""
+"<p style=\"font-size:150%\">Diolch yn fawr am lenwi'n holiadur; rydym yn "
+"falch o glywed ei fod wedi'i adfer i'w ddefnyddio.</p>\n"
-#: perllib/Page.pm:461
-msgid "your empty property will not be posted"
-msgstr "ni fydd eich eiddo gwag yn cael ei bostio"
+#: templates/web/default/questionnaire/completed.html:11
+#, fuzzy
+msgid ""
+"<p style=\"font-size:150%%\">We&rsquo;re sorry to hear that. We have two "
+"suggestions: why not try\n"
+"<a href=\"%s\">writing direct to your councillor(s)</a>\n"
+"or, if it&rsquo;s an empty property that could be returned to use by local "
+"people working together,\n"
+"why not <a href=\"http://www.pledgebank.com/new\">make and publicise a "
+"pledge</a>?\n"
+"</p>"
+msgstr ""
+"<p style=\"font-size:150%\">Mae'n flin gennym glywed hynny. Mae gennym ddau "
+"awgrym: beth am roi cynnig ar\n"
+"<a href=\"http://www.writetothem.com/\">ysgrifennu'n uniongyrchol at eich "
+"cynghorwr(wyr)</a>\n"
+"neu, os yw'n eiddo gwag y gallai pobl leol gydweithio er mwyn ei adfer i'w "
+"ddefnyddio,\n"
+"beth am <a href=\"http://www.pledgebank.com/new\">wneud adduned a'i "
+"chyhoeddi</a>?\n"
+"</p>\n"
-#: perllib/Page.pm:462
+#: templates/web/default/questionnaire/index.html:37
+#, fuzzy
msgid ""
-"we'll hang on to your empty property report while you're checking your email."
+"<p>Getting empty homes back into use can be difficult, but by now a good "
+"council\n"
+"will have made a lot of progress and reported what they have done on the\n"
+"website. Even so properties can remain empty for many months if the owner "
+"is\n"
+"unwilling or the property is in very poor repair. If nothing has happened "
+"or\n"
+"you are not satisfied with the progress the council is making, now is the "
+"right\n"
+"time to say so. We think it&rsquo;s a good idea to contact some other people "
+"who\n"
+"may be able to help or put pressure on the council For advice on how to do\n"
+"this and other useful information please go to <a\n"
+"href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://www."
+"emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
msgstr ""
-"byddwn yn dal gafael ar eich hysbysiad am eiddo gwag wrth i chi wirio eich e-"
-"bost."
+"<p>Mae cael eiddo gwag yn ôl mewn defnydd yn gallu bod yn anodd, ond erbyn "
+"hyn,\n"
+"bydd cyngor da wedi gwneud eithaf tipyn o gynnydd ac wedi rhoi gwybod beth "
+"maent\n"
+"wedi'i wneud ar y wefan. Er hynny, gall eiddo gwag barhau'n wag am fisoedd "
+"os yw'r perchennog\n"
+"yn amharod neu os yw cyflwr yr eiddo'n wael. Os nad oes unrhyw beth wedi "
+"digwydd neu\n"
+"os nad ydych yn fodlon â'r cynnydd y mae'r cyngor yn ei wneud, nawr yw'r "
+"amser priodol\n"
+"i ddweud hynny. Rydym o'r farn bod cysylltu â phobl eraill a allai helpu neu "
+"roi pwysau\n"
+"ar y cyngor yn syniad da. I gael cyngor ar sut i wneud hyn \n"
+"a gwybodaeth ddefnyddiol arall, ewch i <a\n"
+"href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://www."
+"emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
-#: perllib/Page.pm:464
-msgid "your update will not be posted"
-msgstr "ni fydd eich diweddariad yn cael ei bostio"
+#: templates/web/default/questionnaire/index.html:28
+#, fuzzy
+msgid ""
+"<p>Getting empty homes back into use can be difficult. You shouldn&rsquo;t "
+"expect\n"
+"the property to be back into use yet. But a good council will have started "
+"work\n"
+"and should have reported what they have done on the website. If you are not\n"
+"satisfied with progress or information from the council, now is the right "
+"time\n"
+"to say. You may also want to try contacting some other people who may be "
+"able\n"
+"to help. For advice on how to do this and other useful information please\n"
+"go to <a href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://"
+"www.emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
+msgstr ""
+"<p>Mae cael eiddo gwag yn ôl mewn defnydd yn gallu bod yn anodd. Ni ddylech "
+"ddisgwyl\n"
+"bod yr eiddo yn ôl mewn defnydd eto. Fodd bynnag, bydd cyngor da eisoes wedi "
+"dechrau gweithio\n"
+"a dylent fod wedi rhoi gwybod beth maent wedi'i wneud ar y wefan. Os nad "
+"ydych yn fodlon\n"
+"â'r cynnydd neu'r wybodaeth gan y cyngor, nawr yw'r amser priodol i ddweud "
+"hynny.\n"
+"Efallai hefyd y byddwch eisiau rhoi cynnig ar gysylltu â rhai pobl eraill a "
+"all helpu.\n"
+"I gael cyngor ar sut i wneud hyn ac am wybodaeth ddefnyddiol arall, ewch i \n"
+"<a href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://www."
+"emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
-#: perllib/Page.pm:465
-msgid "we'll hang on to your update while you're checking your email."
-msgstr "byddwn yn dal gafael ar eich diweddariad wrth i chi wirio eich e-bost."
+#: templates/web/default/questionnaire/completed.html:5
+#, fuzzy
+msgid ""
+"<p>Thank you very much for filling in our questionnaire; if you\n"
+"get some more information about the status of your empty property, please "
+"come back to the\n"
+"site and leave an update.</p>"
+msgstr ""
+"<p>Diolch yn fawr am lenwi ein holiadur; os ydych yn cael \n"
+"mwy o wybodaeth am statws eich eiddo gwag, dewch yn ôl i'r safle\n"
+"a rhowch ddiweddariad yno.</p>\n"
-#: perllib/Page.pm:467
-msgid "your alert will not be activated"
-msgstr "ni fydd eich hysbysiad yn cael ei weithredu"
+#: templates/web/default/around/display_location.html:93
+#: templates/web/default/around/display_location.html:95
+#, fuzzy
+msgid ""
+"<small>If you cannot see the map, <a href='%s' rel='nofollow'>skip this "
+"step</a>.</small>"
+msgstr ""
+"<small>Os na allwch weld y map, <a href='%s' rel='nofollow'>ewch heibio\n"
+" i'r cam hwn</a>.</small>"
-#: perllib/Page.pm:468
-msgid "we'll hang on to your alert while you're checking your email."
-msgstr "byddwn yn dal gafael ar eich hysbysiad wrth i chi wirio eich e-bost."
+#: templates/web/default/admin/index.html:14
+#, fuzzy
+msgid "<strong>%d</strong> live empty properties"
+msgstr "Adrodd am eiddo gwag a gweld y rhain"
-#: perllib/Page.pm:475
-#, perl-format
-msgid ""
-"<h1>Nearly Done! Now check your email...</h1>\n"
-"<p>The confirmation email <strong>may</strong> take a few minutes to arrive "
-"&mdash; <em>please</em> be patient.</p>\n"
-"<p>If you use web-based email or have 'junk mail' filters, you may wish to "
-"check your bulk/spam mail folders: sometimes, our messages are marked that "
-"way.</p>\n"
-"<p>You must now click the link in the email we've just sent you &mdash;\n"
-"if you do not, %s.</p>\n"
-"<p>(Don't worry &mdash; %s)</p>\n"
-msgstr ""
-"<h1>Bron â gorffen! Nawr, gwiriwch eich e-bost...</h1>\n"
-"<p> <strong>Gallai'r</strong> e-bost o gadarnhad gymryd rhai munudau i "
-"gyrraedd &mdash; <em>byddwch</em> yn amyneddgar.</p>\n"
-"<p>Os ydych yn defnyddio system e-bost ar y we neu os oes gennych hidlydd "
-"'negeseuon sgrwtsh', efallai y byddwch am wirio eich ffolderi post sbam/"
-"swmp: weithiau, caiff ein negeseuon eu marcio fel hynny.</p>\n"
-"<p>Nawr, mae'n rhaid i chi glicio'r ddolen yn yr e-bost yr ydym newydd ei "
-"anfon atoch &mdash;\n"
-"os nad ydych, %s.</p>\n"
-"<p>(Peidiwch â phoeni &mdash; %s)</p>\n"
-
-#: perllib/Page.pm:503
-msgid "today"
-msgstr "heddiw"
+#: templates/web/default/static/about.html:1
+#: templates/web/default/static/about.html:3
+#: templates/web/emptyhomes/header.html:34
+#: templates/web/emptyhomes/static/about.html:1
+#: templates/web/emptyhomes/static/about.html:3
+msgid "About us"
+msgstr "Amdanom ni"
-#: perllib/Page.pm:527
-msgid "less than a minute"
-msgstr "llai na munud"
+#: templates/web/default/admin/council_contacts.html:50
+msgid "Add new category"
+msgstr ""
-#: perllib/Page.pm:530
-#, fuzzy, perl-format
-msgid "%d week"
-msgstr "wythnos"
+#: templates/web/default/auth/change_password.html:30
+msgid "Again:"
+msgstr ""
-#: perllib/Page.pm:530
-#, fuzzy, perl-format
-msgid "%d weeks"
-msgstr "wythnos"
+#: templates/web/default/admin/timeline.html:37
+msgid "Alert %d created for %s, type %s, parameters %s / %s"
+msgstr ""
-#: perllib/Page.pm:531
-#, fuzzy, perl-format
-msgid "%d day"
-msgstr "diwrnod"
+#: templates/web/default/admin/timeline.html:39
+msgid "Alert %d disabled (created %s)"
+msgstr ""
-#: perllib/Page.pm:531
-#, fuzzy, perl-format
-msgid "%d days"
-msgstr "diwrnod"
+#: templates/web/default/report/display.html:133
+msgid "Alert me to future updates"
+msgstr "Rhowch wybod i mi am ddiweddariadau yn y dyfodol"
-#: perllib/Page.pm:532
-#, fuzzy, perl-format
-msgid "%d hour"
-msgstr "awr"
+#: templates/web/default/admin/council_list.html:44
+msgid "All confirmed"
+msgstr ""
-#: perllib/Page.pm:532
-#, fuzzy, perl-format
-msgid "%d hours"
-msgstr "awr"
+#: templates/web/default/footer.html:7 templates/web/emptyhomes/header.html:31
+#: templates/web/fiksgatami/footer.html:6
+msgid "Reports"
+msgstr "Adroddiadau"
-#: perllib/Page.pm:533
-#, fuzzy, perl-format
-msgid "%d minute"
-msgstr "munud"
+#: templates/web/default/report/new/some_councils_text.html:2
+msgid "All the information you provide here will be sent to"
+msgstr ""
-#: perllib/Page.pm:533
-#, fuzzy, perl-format
-msgid "%d minutes"
-msgstr "munud"
+#: templates/web/default/report/new/all_councils_text.html:3
+#: templates/web/default/report/new/all_councils_text.html:5
+#, fuzzy
+msgid ""
+"All the information you provide here will be sent to <strong>%s</strong> or "
+"a relevant local body such as <strong>TfL</strong>, via the London Report-It "
+"system."
+msgstr ""
+"Bydd yr holl wybodaeth a ddarperir gennych yma'n cael ei hanfon at <strong>%"
+"s</strong>.\n"
+"Ar y safle, byddwn yn dangos pwnc a manylion yr eiddo gwag,\n"
+"ynghyd â'ch enw os ydych yn rhoi caniatâd i ni."
-#: perllib/Page.pm:553
-#, perl-format
-msgid "%s, reported anonymously at %s"
-msgstr "Rhoddodd %s wybod yn ddi-enw am %s"
+#: templates/web/default/report/new/all_councils_text.html:10
+#: templates/web/default/report/new/all_councils_text.html:12
+msgid ""
+"All the information you provide here will be sent to <strong>%s</strong>."
+msgstr ""
-#: perllib/Page.pm:555
-#, perl-format
-msgid "%s, reported by %s at %s"
-msgstr "Adroddwyd am %s gan %s am %s"
+#: templates/web/emptyhomes/report/new/all_councils_text.html:2
+#: templates/web/emptyhomes/report/new/all_councils_text.html:4
+#, fuzzy
+msgid ""
+"All the information you provide here will be sent to <strong>%s</strong>. On "
+"the site, we will show the subject and details of the empty property, plus "
+"your name if you give us permission."
+msgstr ""
+"Bydd yr holl wybodaeth a ddarperir gennych yma'n cael ei hanfon at <strong>%"
+"s</strong>.\n"
+"Ar y safle, byddwn yn dangos pwnc a manylion yr eiddo gwag,\n"
+"ynghyd â'ch enw os ydych yn rhoi caniatâd i ni."
-#: perllib/Page.pm:558 perllib/Page.pm:560 perllib/Page.pm:566
-#: perllib/Page.pm:568 web/index.cgi:408 web/index.cgi:594 web/index.cgi:603
-#: bin/send-reports:186 bin/send-reports:195
-msgid "Other"
-msgstr "Arall"
+#: templates/web/default/questionnaire/index.html:62
+msgid "An update marked this empty property as returned to use."
+msgstr ""
+"Fe wnaeth diweddariad gofnodi bod yr eiddo hwn wedi cael ei adfer i'w "
+"ddefnyddio."
-#: perllib/Page.pm:559
-#, perl-format
-msgid "Reported by %s in the %s category anonymously at %s"
-msgstr "Adroddwyd am hyn gan %s yn y categori %s yn ddi-enw, am %s"
+#: templates/web/default/admin/list_updates.html:10
+#: templates/web/default/admin/search_reports.html:18
+msgid "Anonymous"
+msgstr "Di-enw"
-#: perllib/Page.pm:561
-#, perl-format
-msgid "Reported by %s in the %s category by %s at %s"
-msgstr "Adroddwyd gan %s yn y categori %s trwy %s am %s"
+#: templates/web/default/admin/report_edit.html:17
+#: templates/web/default/admin/update_edit.html:14
+#, fuzzy
+msgid "Anonymous:"
+msgstr "Di-enw"
-#: perllib/Page.pm:563
-#, perl-format
-msgid "Reported by %s anonymously at %s"
-msgstr "Adroddwyd gan %s yn ddi-enw am %s"
+#: templates/web/fiksgatami/footer.html:15
+#, fuzzy
+msgid ""
+"Built by <a href=\"http://www.mysociety.org/\">mySociety</a> and maintained "
+"by <a href=\"http://www.nuug.no/\">NUUG</a>, using some <a href=\"http://"
+"github.com/mysociety/fixmystreet\">clever</a>&nbsp;<a href=\"https://secure."
+"mysociety.org/cvstrac/dir?d=mysociety/services/TilMa\">code</a>."
+msgstr ""
+"Adeiladwyd gan <a href=\"http://www.mysociety.org/\">mySociety</a>, gan "
+"ddefnyddio tipyn o <a href=\"https://secure.mysociety.org/cvstrac/dir?"
+"d=mysociety/services/TilMa\">god</a><a href=\"http://github.com/mysociety/"
+"fixmystreet\">clyfar</a>&nbsp;."
-#: perllib/Page.pm:565
-#, perl-format
-msgid "Reported by %s by %s at %s"
-msgstr "Adroddwyd gan %s trwy %s am %s"
+#: templates/web/default/footer.html:16
+msgid ""
+"Built by <a href=\"http://www.mysociety.org/\">mySociety</a>, using some <a "
+"href=\"http://github.com/mysociety/fixmystreet\">clever</a>&nbsp;<a href="
+"\"https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa"
+"\">code</a>."
+msgstr ""
+"Adeiladwyd gan <a href=\"http://www.mysociety.org/\">mySociety</a>, gan "
+"ddefnyddio tipyn o <a href=\"https://secure.mysociety.org/cvstrac/dir?"
+"d=mysociety/services/TilMa\">god</a><a href=\"http://github.com/mysociety/"
+"fixmystreet\">clyfar</a>&nbsp;."
-#: perllib/Page.pm:567
-#, perl-format
-msgid "Reported in the %s category anonymously at %s"
-msgstr "Adroddiwyd yn y categori %s yn ddi-enw am %s"
+#: templates/web/default/report/new/fill_in_details.html:141
+msgid "Can we show your name on the site?"
+msgstr "Allwn ni ddangos eich enw chi ar y safle?"
-#: perllib/Page.pm:569
-#, perl-format
-msgid "Reported in the %s category by %s at %s"
-msgstr "Adroddwyd yn y categori %s gan %s am %s"
+#: templates/web/default/report/display.html:87
+#: templates/web/default/report/new/fill_in_details.html:143
+msgid "Can we show your name publicly?"
+msgstr ""
-#: perllib/Page.pm:571 web/contact.cgi:212
-#, perl-format
-msgid "Reported anonymously at %s"
-msgstr "Adroddwyd yn ddi-enw am %s"
+#: templates/web/default/admin/council_contacts.html:19
+#: templates/web/default/admin/search_reports.html:17
+#, fuzzy
+msgid "Category"
+msgstr "Categori:"
-#: perllib/Page.pm:573 web/contact.cgi:213
-#, perl-format
-msgid "Reported by %s at %s"
-msgstr "Adroddwyd gan %s am %s"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:478
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:502
+#: templates/web/default/admin/council_contacts.html:56
+#: templates/web/default/admin/council_edit.html:23
+#: templates/web/default/admin/report_edit.html:26
+msgid "Category:"
+msgstr "Categori:"
-#: perllib/Page.pm:578
-msgid "the map was not used so pin location may be inaccurate"
+#: bin/send-reports:170
+#, fuzzy
+msgid "Category: %s"
+msgstr "Categori:"
+
+#: templates/web/default/auth/change_password.html:1
+#: templates/web/default/auth/change_password.html:3
+#: templates/web/default/auth/change_password.html:35
+msgid "Change Password"
msgstr ""
-"ni ddefnyddiwyd y map felly mae'n bosibl na fydd lleoliad y pin yn gywir"
-#: perllib/Page.pm:591
-#, perl-format
-msgid "Sent to %s %s later"
-msgstr "Anfonwyd at %s %s yn ddiweddarach"
+#: templates/web/default/around/display_location.html:111
+#: templates/web/default/around/display_location.html:113
+msgid "Closest nearby empty properties <small>(within&nbsp;%skm)</small>"
+msgstr "Yr eiddo gwag cyfagos agosaf <small>(within&nbsp;%skm)</small>"
-#: perllib/Page.pm:595
-msgid "Not reported to council"
-msgstr "Ni roddwyd gwybod i'r cyngor am hyn"
+#: templates/web/default/admin/report_edit.html:15
+msgid "Co-ordinates:"
+msgstr ""
-#: perllib/Page.pm:646 web-admin/index.cgi:523 web-admin/index.cgi:647
-msgid "Updates"
-msgstr "Diweddariadau"
+#: templates/web/default/admin/list_updates.html:11
+#: templates/web/default/admin/search_reports.html:19
+msgid "Cobrand"
+msgstr ""
-#: perllib/Page.pm:650
-#, perl-format
-msgid "Posted by %s at %s"
-msgstr "Cofnodwyd gan %s am %s"
+#: templates/web/default/admin/report_edit.html:36
+#: templates/web/default/admin/update_edit.html:26
+msgid "Cobrand data:"
+msgstr ""
-#: perllib/Page.pm:652
-#, perl-format
-msgid "Posted anonymously at %s"
-msgstr "Cofnodwyd yn ddi-enw am %s"
+#: templates/web/default/admin/report_edit.html:35
+#: templates/web/default/admin/update_edit.html:25
+msgid "Cobrand:"
+msgstr ""
-#: perllib/Page.pm:655
-msgid "marked as returned to use"
-msgstr "cofnodwyd bod hyn wedi'i adfer i'w ddefnyddio"
+#: templates/web/default/admin/council_contacts.html:26
+#, fuzzy
+msgid "Confirm"
+msgstr "Cadarnhau"
-#: perllib/Page.pm:656
-msgid "reopened"
-msgstr "wedi'i ailagor"
+#: templates/web/default/auth/token.html:1
+#, fuzzy
+msgid "Confirm account"
+msgstr "Cadarnhau"
-#: perllib/Page.pm:664 web/index.cgi:1106
-msgid "Offensive? Unsuitable? Tell us"
-msgstr "Sarhaus? Anaddas? Rhowch wybod i ni"
+#: templates/web/default/questionnaire/creator_fixed.html:1
+#: templates/web/default/tokens/confirm_problem.html:1
+#: templates/web/default/tokens/confirm_problem.html:3
+#: templates/web/default/tokens/confirm_update.html:1
+#: templates/web/default/tokens/confirm_update.html:3
+#: templates/web/emptyhomes/tokens/confirm_problem.html:1
+#: templates/web/emptyhomes/tokens/confirm_problem.html:3
+msgid "Confirmation"
+msgstr "Cadarnhau"
-#: perllib/Page.pm:691
-msgid "That postcode was not recognised, sorry."
-msgstr "Ni chafodd y cod post hwnnw ei gydnabod, sori."
+#: templates/web/default/admin/council_contacts.html:21
+#: templates/web/default/admin/council_contacts.html:66
+#: templates/web/default/admin/council_edit.html:28
+#: templates/web/default/admin/council_edit.html:43
+#, fuzzy
+msgid "Confirmed"
+msgstr "Cadarnhau"
-#: perllib/Page.pm:697
-msgid ""
-"Sorry, that appears to be a Crown dependency postcode, which we don't cover."
-msgstr ""
+#: templates/web/default/admin/report_edit.html:31
+#: templates/web/default/admin/search_reports.html:47
+#, fuzzy
+msgid "Confirmed:"
+msgstr "Cadarnhau"
-#: perllib/Page.pm:700 perllib/FixMyStreet/Geocode.pm:145
-msgid ""
-"We do not cover Northern Ireland, I'm afraid, as our licence doesn't include "
-"any maps for the region."
-msgstr ""
-"Nid ydym yn ymdrin â Gogledd Iwerddon, yn anffodus, gan nad yw ein trwydded "
-"yn cynnwys unrhyw fapiau ar gyfer y rhanbarth."
+#: templates/web/default/footer.html:10 templates/web/fiksgatami/footer.html:9
+msgid "Contact"
+msgstr "Cysylltu"
-#: perllib/Page.pm:731
-msgid "Please upload a JPEG image only"
-msgstr "Dim ond llun JPEG y dylech lwytho i fyny."
+#: templates/web/default/contact/index.html:1
+#: templates/web/default/contact/index.html:2
+#: templates/web/default/contact/submit.html:1
+msgid "Contact Us"
+msgstr "Cysylltwch â Ni"
-#: perllib/FixMyStreet/Alert.pm:147
+#: templates/web/default/contact/index.html:6
+#: templates/web/default/contact/submit.html:3
+msgid "Contact the team"
+msgstr "Cysylltu â'r tîm"
+
+#: templates/web/default/admin/search_reports.html:16
#, fuzzy
-msgid "This report is currently marked as returned to use."
-msgstr "Mae'r eiddo gwag hwn wedi cael ei adfer i'w ddefnyddio"
+msgid "Council"
+msgstr "cyngor"
-#: perllib/FixMyStreet/Alert.pm:148
-msgid "This report is currently marked as open."
+#: perllib/FixMyStreet/App/Controller/Admin.pm:598
+#: templates/web/default/admin/council_list.html:1
+#, fuzzy
+msgid "Council contacts"
+msgstr "Cysylltwch â Ni"
+
+#: templates/web/default/admin/council_contacts.html:1
+#: templates/web/default/admin/council_edit.html:1
+msgid "Council contacts for %s"
msgstr ""
-#: perllib/FixMyStreet/Alert.pm:313
-#, fuzzy, perl-format
-msgid "Report on %s"
-msgstr "Adroddiadau"
+#: templates/web/default/admin/council_list.html:36
+#, fuzzy
+msgid "Councils"
+msgstr "cyngor"
-#: perllib/FixMyStreet/Geocode.pm:73
-msgid ""
-"Sorry, that location appears to be too general; please be more specific."
-msgstr ""
-"Sori, mae'n ymddangos bod y lleoliad hwnnw'n rhy gyffredinol; rhowch gynnig "
-"arall arni."
+#: templates/web/default/email_sent.html:1
+#, fuzzy
+msgid "Create a report"
+msgstr "Categori:"
-#: perllib/FixMyStreet/Geocode.pm:83
-msgid "That location does not appear to be in Britain; please try again."
-msgstr ""
-"Nid yw'n ymddangos bod y lleoliad hwnnw ym Mhrydain; rhowch gynnig arall "
-"arni."
+#: templates/web/default/admin/council_contacts.html:80
+#, fuzzy
+msgid "Create category"
+msgstr "Categori:"
-#: perllib/FixMyStreet/Geocode.pm:105 perllib/FixMyStreet/Geocode.pm:140
-msgid "Sorry, we could not find that location."
-msgstr "Sori, ni fu modd i ni ddod o hyd i'r lleoliad hwnnw."
+#: templates/web/default/admin/list_updates.html:9
+#: templates/web/default/admin/search_reports.html:20
+msgid "Created"
+msgstr ""
-#: perllib/FixMyStreet/Geocode.pm:138
-msgid "Sorry, we could not parse that location. Please try again."
+#: templates/web/default/admin/report_edit.html:30
+#: templates/web/default/admin/update_edit.html:27
+msgid "Created:"
msgstr ""
-"Sori, ni fu modd i ni ddosrannu'r lleoliad hwnnw. Rhowch gynnig arall arni."
-#: perllib/FixMyStreet/Geocode.pm:158
-msgid ""
-"We found more than one match for that location. We show up to ten matches, "
-"please try a different search if yours is not here."
+#: templates/web/default/admin/council_list.html:40
+msgid "Currently has 1+ deleted"
msgstr ""
-"Darganfuom fwy nag un man sy'n cyfateb i'r lleoliad hwnnw. Rydym yn dangos "
-"hyd at ddeg cyfatebiaeth, rhowch gynnig ar chwiliad gwahanol os nad yw'ch "
-"lleoliad chi yno."
-#: perllib/FixMyStreet/Geocode.pm:173
-msgid "More than one match"
+#: templates/web/default/admin/council_contacts.html:22
+#: templates/web/default/admin/council_contacts.html:69
+#: templates/web/default/admin/council_edit.html:29
+#: templates/web/default/admin/council_edit.html:44
+msgid "Deleted"
msgstr ""
-#: perllib/FixMyStreet/Map/Bing.pm:41
-#: perllib/FixMyStreet/Map/OSM/StreetView.pm:42
-#: perllib/FixMyStreet/Map/Google.pm:41
-#: perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm:51
-#: perllib/FixMyStreet/Map/Tilma/Original/StreetView.pm:21
-msgid ""
-"Map contains Ordnance Survey data &copy; Crown copyright and database right "
-"2010."
+#: templates/web/default/admin/report_edit.html:14
+#: templates/web/default/report/new/fill_in_details.html:100
+msgid "Details:"
+msgstr "Manylion:"
+
+#: templates/web/default/admin/council_list.html:23
+msgid "Diligency prize league table"
msgstr ""
-#: perllib/FixMyStreet/Map/OSM.pm:76
-msgid ""
-"Map &copy; <a id=\"osm_link\" href=\"http://www.openstreetmap.org/"
-"\">OpenStreetMap</a> and contributors, <a href=\"http://creativecommons.org/"
-"licenses/by-sa/2.0/\">CC-BY-SA</a>"
+#: templates/web/default/questionnaire/index.html:72
+msgid "Don&rsquo;t know"
+msgstr "Ddim yn gwybod"
+
+#: templates/web/default/admin/list_updates.html:31
+#: templates/web/default/admin/search_reports.html:52
+msgid "Edit"
msgstr ""
-#: perllib/FixMyStreet/Map/OSM.pm:115
-#: perllib/FixMyStreet/Map/Tilma/Original.pm:155
-msgid "Empty property"
-msgstr "Eiddo gwag"
+#: templates/web/default/admin/report_edit.html:1
+#, fuzzy
+msgid "Editing empty property %d"
+msgstr "Gweld eiddo gwag"
-#: perllib/FixMyStreet/Map/BingOL.pm:44
-msgid ""
-"Map contains Ordnance Survey data &copy; Crown copyright and database right "
-"2010. Microsoft"
+#: templates/web/default/admin/update_edit.html:1
+#, fuzzy
+msgid "Editing update %d"
+msgstr "Cyflwyno'ch diweddariad"
+
+#: templates/web/default/admin/council_edit.html:45
+msgid "Editor"
msgstr ""
-#: perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm:51
-#: perllib/FixMyStreet/Map/Tilma/Original/1_10k.pm:21
+#: templates/web/default/admin/council_contacts.html:20
+#: templates/web/default/admin/council_edit.html:42
+#: templates/web/default/admin/list_updates.html:8
+#: templates/web/default/admin/search_reports.html:15
+#: templates/web/default/report/display.html:95
#, fuzzy
-msgid ""
-"&copy; Crown copyright. All rights reserved. Ministry of Justice "
-"100037819&nbsp;2008."
-msgstr "Hawlfraint y Goron. Cedwir pob hawl. Y Weinyddiaeth Amddiffyn"
+msgid "Email"
+msgstr "E-bost:"
-#: perllib/FixMyStreet/Map/Tilma/Original.pm:86
-msgid "Unable to fetch the map tiles from the tile server."
+#: templates/web/default/around/display_location.html:77
+msgid "Email me new local empty properties"
+msgstr "Anfonwch fanylion eiddo gwag lleol newydd ataf i drwy'r e-bost"
+
+#: templates/web/default/report/display.html:42
+msgid "Email me updates"
+msgstr "Anfonwch ddiweddariadau ataf i drwy'r e-bost"
+
+#: templates/web/default/auth/general.html:47
+msgid "Email the details I need to the address I entered above"
msgstr ""
-#: perllib/Problems.pm:148
-#, perl-format
-msgid "<big>%s</big> report in past week"
-msgid_plural "<big>%s</big> reports in past week"
-msgstr[0] "<big>%s</big> adroddiad o fewn yr wythnos ddiwethaf"
-msgstr[1] "<big>%s</big> o adroddiadau o fewn yr wythnos ddiwethaf"
+#: templates/web/default/admin/council_contacts.html:61
+#: templates/web/default/admin/council_edit.html:26
+#: templates/web/default/admin/report_edit.html:28
+#: templates/web/default/admin/update_edit.html:24
+#: templates/web/default/alert/updates.html:13
+#: templates/web/default/auth/general.html:29
+#: templates/web/default/report/display.html:46
+#: templates/web/default/report/new/fill_in_details.html:153
+msgid "Email:"
+msgstr "E-bost:"
-#: perllib/Problems.pm:153
-#, perl-format
-msgid "<big>%s</big> report recently"
-msgid_plural "<big>%s</big> reports recently"
-msgstr[0] "<big>%s</big> adroddiad yn ddiweddar"
-msgstr[1] "<big>%s</big> o adroddiadau yn ddiweddar"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:463
+msgid "Empty flat or maisonette"
+msgstr "Fflat neu fflat deulawr gwag"
-#: perllib/Problems.pm:159
-msgid "reportemptyhomes.com updates"
-msgstr "Diweddariadau i reportemptyhomes.com"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:462
+msgid "Empty house or bungalow"
+msgstr "TÅ· neu fyngalo gwag"
-#: perllib/Problems.pm:162 perllib/Problems.pm:166
-#, perl-format
-msgid "<big>%s</big> returned to use in past month"
-msgid_plural "<big>%s</big> returned to use in past month"
-msgstr[0] "Dychwelwyd <big>%s</big> i'w ddefnyddio yn y mis diwethaf"
-msgstr[1] "Dychwelwyd <big>%s</big> i'w defnyddio yn y mis diwethaf"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:465
+msgid "Empty office or other commercial"
+msgstr "Swyddfa wag neu fan masnachol gwag arall"
-#: perllib/Problems.pm:167
-#, perl-format
-msgid "<big>%s</big> update on reports"
-msgid_plural "<big>%s</big> updates on reports"
-msgstr[0] "<big>%s</big> diweddariad ar adroddiadau"
-msgstr[1] "<big>%s</big> diweddariad ar adroddiadau"
+#: templates/web/emptyhomes/report/new/form_heading.html:1
+msgid "Empty property details form"
+msgstr "Ffurflen manylion eiddo gwag"
-#: web/about.cgi:24
-msgid ""
-"<h2>The Empty Homes Agency</h2>\n"
-"<p>The Empty Homes agency is an independent campaigning charity. We are not\n"
-"part of government, and have no formal links with local councils although "
-"we\n"
-"work in cooperation with both. We exist to highlight the waste of empty\n"
-"property and work with others to devise and promote sustainable solutions "
-"to\n"
-"bring empty property back into use. We are based in London but work across\n"
-"England. We also work in partnership with other charities across the UK.</"
-"p>\n"
-msgstr ""
-"<h2>Yr Asiantaeth Tai Gwag</h2>\n"
-"<p>Mae'r Asiantaeth Tai Gwag yn elusen ymgyrchu annibynnol. Nid ydym yn rhan "
-"o'r\n"
-"llywodraeth, ac nid oes gennym unrhyw gysylltiadau ffurfiol â chynghorau "
-"lleol er ein bod\n"
-"yn cydweithio â'r naill a'r llall. Rydym yn bodoli er mwyn amlygu gwastraff "
-"yr eiddo gwag ac\n"
-"yn gweithio gyda eraill er mwyn dyfeisio a hyrwyddo atebion cynaliadwy er "
-"mwyn adfer \n"
-"eiddo gwag yn eiddo y mae pobl yn byw ynddynt eto. Rydym wedi'n lleoli yn "
-"Llundain ond\n"
-"rydym yn gweithio ledled Lloegr. Rydym hefyd yn gweithio mewn partneriaeth "
-"ag elusennau eraill ledled y DU.</p>\n"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:466
+msgid "Empty pub or bar"
+msgstr "Tafarn neu far gwag"
-#: web/about.cgi:34
-msgid ""
-"<h2>Shelter Cymru</h2>\n"
-"Shelter Cymru is Wales&rsquo; people and homes charity and wants everyone in "
-"Wales to\n"
-"have a decent home. We believe a home is a fundamental right and essential "
-"to\n"
-"the health and well-being of people and communities. We work for people in\n"
-"housing need. We have offices all over Wales and prevent people from losing\n"
-"their homes by offering free, confidential and independent advice. When\n"
-"necessary we constructively challenge on behalf of people to ensure they "
-"are\n"
-"properly assisted and to improve practice and learning. We believe that\n"
-"bringing empty homes back into use can make a significant contribution to "
-"the\n"
-"supply of affordable homes in Wales.\n"
-"<a href=\"http://www.sheltercymru.org.uk/shelter/advice/pdetail.asp?cat=20"
-"\">Further information about our work on\n"
-"empty homes</a>.\n"
-msgstr ""
-"<h2>Shelter Cymru</h2>\n"
-"Shelter Cymru yw&rsquo;r elusen yng Nghymru ar gyfer pobl a chartrefi ac "
-"rydym\n"
-"eisiau i bawb yng Nghymru gael cartref addas. Credwn fod cartref yn hawl\n"
-"sylfaenol a&rsquo;i fod yn hanfodol i iechyd a lles pobl a chymunedau. Rydym "
-"yn\n"
-"gweithio dros bobl sydd mewn angen am dai. Mae gennym swyddfeydd ar draws "
-"Cymru\n"
-"ac rydym yn atal pobl rhag colli eu cartrefi drwy gynnig cyngor annibynnol,\n"
-"cyfrinachol, am ddim. Pan fo&rsquo;r angen, rydym yn herio&rsquo;n adeiladol "
-"ar ran pobl er\n"
-"mwyn sicrhau eu bod yn cael eu cynorthwyo&rsquo;n gywir ac i wella ymarfer a "
-"dysgu.\n"
-"Mae Shelter Cymru yn credu y gall gwneud gwell defnydd o gartrefi gwag\n"
-"gyfrannu&rsquo;n sylweddol at y ddaprariaeth o dai fforddiadwy yng Nghymru.\n"
-"<a href='http://www.sheltercymru.org.uk/shelter/cymraeg/advice/pdetail.asp?"
-"cat=20'>Gwybodaeth\n"
-"bellach am ein gwaith ar gartrefi gwag</a>.\n"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:467
+msgid "Empty public building - school, hospital, etc."
+msgstr "Adeilad cyhoeddus gwag - ysgol, ysbyty, ac ati."
-#: web/ajax.cgi:55 web/ajax.cgi:76 web/index.cgi:935 web/index.cgi:950
-msgid "(returned to use)"
-msgstr "(wedi'i adfer i'w ddefnyddio)"
+#: templates/web/default/around/around_index.html:10
+#: templates/web/default/around/around_index.html:13
+#: templates/web/default/index.html:40 templates/web/default/index.html:43
+msgid "Enter a nearby GB postcode, or street name and area"
+msgstr "Cofnodwch god post Prydeinig, neu enw stryd ac ardal gerllaw"
-#: web/alert.cgi:33 web/confirm.cgi:49
-msgid "Confirmation"
-msgstr "Cadarnhau"
+#: perllib/FixMyStreet/Cobrand/FiksGataMi.pm:20
+#, fuzzy
+msgid "Enter a nearby postcode, or street name and area"
+msgstr "Cofnodwch god post Prydeinig, neu enw stryd ac ardal gerllaw:"
-#: web/alert.cgi:43
-#, perl-format
-msgid ""
-"Thank you for trying to confirm your alert. We seem to have an error "
-"ourselves\n"
-"though, so <a href=\"%s\">please let us know what went on</a> and we'll look "
-"into it.\n"
-msgstr ""
-"Diolch am geisio cadarnhau eich rhybudd. Fodd bynnag, mae'n ymddangos bod "
-"gennym wall\n"
-"ein hunain, felly <a href=\"%s\">rhowch wybod i ni beth sydd wedi digwydd</"
-"a> ac fe wnawn ni edrych yn fanylach ar hyn.\n"
+#: templates/web/default/index.html:61
+msgid "Enter details of the empty property"
+msgstr "Rhowch fanylion yr eiddo gwag"
-#: web/alert.cgi:59 web/alert.cgi:62 web/alert.cgi:65 web/alert.cgi:274
-#: web/alert.cgi:380
-msgid "Local RSS feeds and email alerts"
-msgstr "Porthiannau RSS a hysbysiadau e-bost lleol"
+#: templates/web/default/auth/token.html:5
+#: templates/web/default/questionnaire/error.html:1
+#: templates/web/default/questionnaire/error.html:3
+#: templates/web/default/tokens/abuse.html:1
+#: templates/web/default/tokens/abuse.html:3
+#: templates/web/default/tokens/error.html:1
+#: templates/web/default/tokens/error.html:3
+msgid "Error"
+msgstr "Gwall"
-#: web/alert.cgi:121
+#: templates/web/default/admin/council_contacts.html:9
+#: templates/web/default/admin/council_edit.html:18
+msgid "Example postcode %s"
+msgstr ""
+
+#: templates/web/default/contact/submit.html:15
+#, fuzzy
msgid ""
-"That location does not appear to be covered by a council, perhaps it is "
-"offshore - please try somewhere more specific."
+"Failed to send message. Please try again, or <a href=\"mailto:%s\">email "
+"us</a>."
msgstr ""
-"Nid yw'n ymddangos bod cyngor yn gyfrifol am y lleoliad hwnnw, efallai ei "
-"fod ar y môr - chwiliwch am fan mwy penodol."
+"Rhowch gynnig eto'n ddiweddarach, neu <a href=\"mailto:%s\">anfonwch neges e-"
+"bost atom</a>i roi gwybod inni."
-#: web/alert.cgi:140 web/alert.cgi:183 web/alert.cgi:200
-#, perl-format
-msgid "Empty properties within %s"
-msgstr "Eiddo gwag yn %s"
+#: templates/web/default/questionnaire/index.html:81
+msgid "First time"
+msgstr "Y tro cyntaf"
-#: web/alert.cgi:152 web/alert.cgi:233
-msgid "Empty properties within the boundary of:"
-msgstr "Eiddo gwag o fewn ffiniau:"
+#: templates/web/default/header.html:16
+#: templates/web/fiksgatami/header.html:14
+msgid "Fix<span id=\"my\">My</span>Street"
+msgstr "Trwsio<span id=\"my\">Fy</span>Stryd"
-#: web/alert.cgi:158 web/alert.cgi:245
-msgid "Or empty properties reported to:"
-msgstr "Neu eiddo gwag y rhoddwyd gwybod amdanynt i:"
+#: templates/web/default/admin/header.html:12
+#, fuzzy
+msgid "reportemptyhomes.com admin:"
+msgstr "reportemptyhomes.com"
-#: web/alert.cgi:160 web/alert.cgi:247
+#: templates/web/default/admin/header.html:3
+#, fuzzy
+msgid "reportemptyhomes.com administration"
+msgstr "Diweddariadau i reportemptyhomes.com"
+
+#: templates/web/default/alert/index.html:6
+msgid ""
+"reportemptyhomes.com has a variety of RSS feeds and email alerts for local "
+"empty properties, including\n"
+"alerts for all empty properties within a particular ward or council, or all "
+"empty properties\n"
+"within a certain distance of a particular location."
+msgstr ""
+"Mae gan reportemptyhomes.com amrywiaeth o borthiannau RSS a hysbysiadau e-"
+"bost ar gyfer eiddo gwag lleol,\n"
+"gan gynnwys hysbysiadau am bob eiddo gwag mewn ward neu gyngor penodol, neu "
+"bob eiddo gwag\n"
+"o fewn pellter penodol o leoliad penodol."
+
+#: templates/web/default/alert/list.html:101
msgid ""
"reportemptyhomes.com sends different categories of empty property\n"
"to the appropriate council, so empty properties within the boundary of a "
@@ -599,232 +791,195 @@ msgstr ""
"rhanbarth a'r cyngor, ond dim ond yn yr hysbysiad \"O fewn y ffin\" \n"
"y bydd yn ymddangos yn achos y cyngor sir."
-#: web/alert.cgi:185
-#, perl-format
-msgid "Empty properties within %s ward"
-msgstr "Eiddo gwag yn ward %s"
-
-#: web/alert.cgi:269
-msgid "Photos of recent nearby reports"
-msgstr "Ffotograffau o adroddiadau cyfagos diweddar"
-
-#: web/alert.cgi:272
-#, perl-format
-msgid "Local RSS feeds and email alerts for &lsquo;%s&rsquo;"
-msgstr "Porthiannau RSS lleol a hysbysiadau e-bost ar gyfer &lsquo;%s&rsquo;"
+#: templates/web/default/front_stats.html:7
+msgid "reportemptyhomes.com updates"
+msgstr "Diweddariadau i reportemptyhomes.com"
-#: web/alert.cgi:286
-#, perl-format
-msgid "Here are the types of local empty property alerts for &lsquo;%s&rsquo;."
+#: templates/web/default/admin/report_edit.html:22
+msgid "Fixed"
msgstr ""
-"Dyma'r mathau o hysbysiadau am eiddo gwag lleol ar gyfer &lsquo;%s&rsquo;."
-#: web/alert.cgi:287
-msgid ""
-"Select which type of alert you&rsquo;d like and click the button for an RSS\n"
-"feed, or enter your email address to subscribe to an email alert."
+#: templates/web/default/admin/search_reports.html:48
+msgid "Fixed:"
msgstr ""
-"Dewiswch y math o hysbysiad yr hoffech ei gael a chliciwch y botwm ar gyfer "
-"porthiant RSS,\n"
-"neu rhowch eich cyfeiriad e-bost i danysgrifio am hysbysiad e-bost."
-#: web/alert.cgi:290
-msgid "The simplest alert is our geographic one:"
-msgstr "Yr hysbysiad symlaf yw ein hysbysiad daearyddol:"
-
-#: web/alert.cgi:291
-#, perl-format
-msgid "Empty properties within %skm of this location"
-msgstr "Eiddo gwag o fewn %skm o'r lleoliad hwn"
-
-#: web/alert.cgi:305
-msgid "(a default distance which covers roughly 200,000 people)"
-msgstr "(pellter rhagosodedig sy'n cynnwys tua 200,000 o bobl)"
-
-#: web/alert.cgi:308
-msgid "RSS feed of nearby empty properties"
-msgstr "Porthiant RSS o eiddo gwag sydd gerllaw"
-
-#: web/alert.cgi:308 web/alert.cgi:367 web/index.cgi:1005 web/index.cgi:1117
-#: web/reports.cgi:254
-msgid "RSS feed"
-msgstr "Porthiant RSS"
+#: templates/web/default/admin/report_edit.html:16
+#, fuzzy
+msgid "For council(s):"
+msgstr "cyngor"
-#: web/alert.cgi:309
-msgid "(alternatively the RSS feed can be customised, within"
-msgstr "(fel arall, gellir addasu'r porthiant RSS yn bersonol, o fewn"
+#: templates/web/default/faq/faq-en-gb.html:1
+#: templates/web/emptyhomes/faq/faq-cy.html:1
+#: templates/web/emptyhomes/faq/faq-en-gb.html:1
+#: templates/web/fiksgatami/faq/faq-nb.html:1
+msgid "Frequently Asked Questions"
+msgstr "Cwestiynau Cyffredin"
-#: web/alert.cgi:319
-msgid ""
-"Or you can subscribe to an alert based upon what ward or council you&rsquo;"
-"re in:"
+#: templates/web/emptyhomes/static/about.html:34
+msgid "Further information about our work on empty homes."
msgstr ""
-"Neu, gallwch danysgrifio am hysbysiad yn ôl pa ward neu gyngor yr ydych yn "
-"byw ynddo:"
-#: web/alert.cgi:323
+#: templates/web/default/index.html:22
+#, fuzzy
+msgid "Get reportemptyhomes.com on your iPhone"
+msgstr "Cael yr Asiantaeth Tai Gwag ar eich iPhone"
+
+#: templates/web/default/alert/list.html:115
msgid "Give me an RSS feed"
msgstr "Rhowch borthiant RSS i mi"
-#: web/alert.cgi:324
-msgid "or"
-msgstr "neu"
+#: templates/web/default/alert/index.html:24
+#: templates/web/default/around/around_index.html:17
+#: templates/web/default/index.html:49
+msgid "Go"
+msgstr "Ewch"
-#: web/alert.cgi:325
-msgid "Your email:"
-msgstr "Eich cyfeiriad e-bost:"
+#: templates/web/default/admin/report_edit.html:37
+#, fuzzy
+msgid "Going to send questionnaire?"
+msgstr "Anfon yr holiadur"
-#: web/alert.cgi:326
-msgid "Subscribe me to an email alert"
-msgstr "Dymunaf danysgrifio i rybuddion drwy e-bost"
+#: templates/web/default/admin/index.html:23
+msgid "Graph of empty property creation by status over time"
+msgstr ""
-#: web/alert.cgi:367
-#, perl-format
-msgid "RSS feed of %s"
-msgstr "Porthiant RSS %s"
+#: templates/web/default/reports/index.html:5
+#: templates/web/emptyhomes/reports/index.html:5
+msgid "Greyed-out lines are councils that no longer exist."
+msgstr "Mae'r llinellau mewn llwyd yn gynghorau nad ydynt yn bodoli mwyach."
-#: web/alert.cgi:381
-msgid ""
-"reportemptyhomes.com has a variety of RSS feeds and email alerts for local "
-"empty properties, including\n"
-"alerts for all empty properties within a particular ward or council, or all "
-"empty properties\n"
-"within a certain distance of a particular location."
-msgstr ""
-"Mae gan reportemptyhomes.com amrywiaeth o borthiannau RSS a hysbysiadau e-"
-"bost ar gyfer eiddo gwag lleol,\n"
-"gan gynnwys hysbysiadau am bob eiddo gwag mewn ward neu gyngor penodol, neu "
-"bob eiddo gwag\n"
-"o fewn pellter penodol o leoliad penodol."
+#: templates/web/default/questionnaire/index.html:63
+msgid "Has this empty property been returned to use?"
+msgstr "A yw'r eiddo gwag hwn wedi cael ei adfer i'w ddefnyddio?"
-#: web/alert.cgi:384
+#: templates/web/default/questionnaire/index.html:76
msgid ""
-"To find out what local alerts we have in your area, council or ward, please "
-"enter your GB\n"
-"postcode or street name and area:"
+"Have you ever reported an empty property to a council before, or is this "
+"your first time?"
msgstr ""
-"I gael gwybod pa hysbysiadau lleol sydd gennym yn eich ardal, cyngor neu "
-"ward chi,\n"
-"cofnodwch eich cod post Prydeinig neu enw stryd ac ardal:"
-
-#: web/alert.cgi:389 web/index.cgi:167
-msgid "Go"
-msgstr "Ewch"
-
-#: web/alert.cgi:415
-msgid "Some photos of recent reports"
-msgstr "Rhai ffotograffau o adroddiadau diweddar"
-
-#: web/alert.cgi:423
-msgid "Please select the feed you want"
-msgstr "Dewiswch y porthiant rydych chi eisiau ei gael"
+"Ydych chi wedi rhoi gwybod i gyngor am eiddo gwag erioed o'r blaen, neu ai "
+"dyma'ch tro cyntaf?"
-#: web/alert.cgi:446
-msgid "Illegal feed selection"
-msgstr "Dewis porthiant annilys"
+#: templates/web/default/footer.html:9 templates/web/emptyhomes/header.html:33
+#: templates/web/fiksgatami/footer.html:8
+msgid "FAQs"
+msgstr "Cwestiynau Cyffredin"
-#: web/alert.cgi:460
-msgid "Receive email when updates are left on this empty property."
+#: templates/web/default/alert/list.html:39
+msgid "Here are the types of local empty property alerts for &lsquo;%s&rsquo;."
msgstr ""
-"Derbyn neges e-bost pan fydd diweddariadau'n cael eu rhoi am yr eiddo gwag "
-"hwn."
+"Dyma'r mathau o hysbysiadau am eiddo gwag lleol ar gyfer &lsquo;%s&rsquo;."
-#: web/alert.cgi:461 web/alert.cgi:488 web/index.cgi:769 web/index.cgi:1111
-#: web-admin/index.cgi:631 web-admin/index.cgi:751
-msgid "Email:"
-msgstr "E-bost:"
+#: templates/web/default/admin/report_edit.html:22
+#: templates/web/default/admin/update_edit.html:19
+#, fuzzy
+msgid "Hidden"
+msgstr "Cuddio pinnau"
-#: web/alert.cgi:462 web/alert.cgi:489 web/index.cgi:1112
-msgid "Subscribe"
-msgstr "Tanysgrifio"
+#: templates/web/default/around/display_location.html:54
+msgid "Hide pins"
+msgstr "Cuddio pinnau"
-#: web/alert.cgi:487
-#, fuzzy
-msgid "Receive alerts on new local empty properties"
-msgstr "Anfonwch fanylion eiddo gwag lleol newydd ataf i drwy'r e-bost"
+#: templates/web/default/around/display_location.html:59
+msgid "Hide stale reports"
+msgstr "Cuddio hen adroddiadau "
-#: web/alert.cgi:515
-msgid "You have successfully subscribed to that alert."
-msgstr "Rydych wedi tanysgrifio'n llwyddiannus i'r hysbysiad hwnnw."
+#: templates/web/default/admin/council_edit.html:38
+msgid "History"
+msgstr ""
-#: web/alert.cgi:522
-msgid "We could not validate that alert."
-msgstr "Ni allem ddilysu'r hysbysiad hwnnw."
+#: templates/web/default/index.html:56
+msgid "How to report an empty property"
+msgstr "Sut i roi gwybod am eiddo gwag"
-#: web/alert.cgi:544
-msgid "You have successfully confirmed your alert."
-msgstr "Rydych wedi cadarnhau'ch hysbysiad yn llwyddiannus."
+#: perllib/FixMyStreet/App/Controller/Admin.pm:516
+msgid "I am afraid you cannot confirm unconfirmed reports."
+msgstr ""
-#: web/alert.cgi:551
-msgid "You have successfully deleted your alert."
-msgstr "Rydych wedi dileu'ch hysbysiad yn llwyddiannus."
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:93
+msgid "I'm afraid we couldn't locate your empty property in the database.\n"
+msgstr ""
+"Yn anffodus ni fu modd i ni ddod o hyd i'ch eiddo gwag yn y gronfa ddata.\n"
-#: web/alert.cgi:571
-msgid "Please enter a valid email address"
-msgstr "Cofnodwch gyfeiriad e-bost dilys"
+#: perllib/FixMyStreet/App/Controller/Tokens.pm:161
+msgid ""
+"I'm afraid we couldn't validate that token. If you've copied the URL from an "
+"email, please check that you copied it exactly.\n"
+msgstr ""
+"Yn anffodus, ni fu modd i ni ddilysu'r dynodiad hwnnw. Os ydych wedi copïo'r "
+"URL o neges e-bost, gwnewch yn siwr eich bod wedi'i gopïo'n union.\n"
-#: web/alert.cgi:572
-msgid "Please select the type of alert you want"
-msgstr "Dewiswch y math o hysbysiad rydych chi eisiau ei gael"
+#: templates/web/default/admin/list_updates.html:5
+#: templates/web/default/admin/search_reports.html:12
+msgid "ID"
+msgstr ""
-#: web/confirm.cgi:39
-#, perl-format
+#: templates/web/default/report/new/no_councils_text.html:11
+#: templates/web/default/report/new/no_councils_text.html:3
msgid ""
-"Thank you for trying to confirm your update or empty property. We seem to "
-"have an\n"
-"error ourselves though, so <a href=\"%s\">please let us know what went on</"
-"a>\n"
-"and we'll look into it.\n"
+"If you submit an empty property here the subject and details of the empty "
+"property will be public, but the empty property will <strong>not</strong> be "
+"reported to the council."
msgstr ""
-"Diolch am geisio cadarnhau eich diweddariad neu eich eiddo gwag. Fodd "
-"bynnag, \n"
-"mae'n ymddangos bod gwall gennym ein hunain, felly <a href=\"%s\">rhowch "
-"wybod i ni beth ddigwyddodd</a>\n"
-"ac fe wnawn ni edrych yn fanylach i hyn.\n"
-#: web/confirm.cgi:101
-#, perl-format
+#: templates/web/emptyhomes/report/new/no_councils_text.html:9
+#, fuzzy
msgid ""
-"You have successfully confirmed your update and you can now <a href=\"%s"
-"\">view it on the site</a>."
+"If you submit a report here it will be left on the site, but not reported to "
+"the council &ndash; please still leave your report, so that we can show to "
+"the council the activity in their area."
msgstr ""
-"Rydych wedi cadarnhau eich diweddariad yn llwyddiannus ac nawr gallwch <a "
-"href=\"%s\">weld hwn ar y safle</a>."
+"<p>Nid oes gennym fanylion eto ar gyfer y cyngor sy'n gyfrifol am \n"
+"y lleoliad hwn. Os ydych yn cyflwyno adroddiad yma, bydd yn cael ei adael ar "
+"y safle, ond\n"
+"ni fydd yn cael ei adrodd i'r cyngor &ndash; gadewch eich adroddiad o hyd, "
+"fel y gallwn\n"
+"ddangos i'r cyngor y gweithgarwch yn ei ardal."
-#: web/confirm.cgi:131
-msgid "Sorry, there has been an error confirming your empty property."
-msgstr "Sori, bu gwall wrth gadarnhau eich eiddo gwag."
+#: templates/web/default/email_sent.html:28
+msgid ""
+"If you use web-based email or have 'junk mail' filters, you may wish to "
+"check your bulk/spam mail folders: sometimes, our messages are marked that "
+"way."
+msgstr ""
-#: web/confirm.cgi:139
+#: templates/web/default/questionnaire/index.html:85
msgid ""
-"Thank you for reporting an empty property on\n"
-"ReportEmptyHomes.com. We have emailed the lead officer for empty homes in "
-"the council\n"
-"responsible with details, and asked them to do whatever they can to get the\n"
-"empty property back into use as soon as possible."
+"If you wish to leave a public update on the empty property, please enter it "
+"here\n"
+"(please note it will not be sent to the council). For example, what was\n"
+"your experience of getting the empty property returned to use?"
msgstr ""
-"Diolch am roi gwybod am eiddo gwag ar\n"
-"ReportEmptyHomes.com. Rydym wedi anfon neges e-bost gyda'r manylion at y "
-"swyddog eiddo gwag yn \n"
-"y cyngor cyfrifol ac wedi gofyn iddynt wneud beth bynnag y gallant i helpu "
-"adfer yr eiddo gwag\n"
-"fel ei fod yn cael ei ddefnyddio eto cyn gynted ag y bo modd."
+"Os ydych yn dymuno gadael diweddariad cyhoeddus am yr eiddo gwag, cofnodwch "
+"hwn yma\n"
+"(sylwer na fydd yn cael ei anfon at y cyngor). Er enghraifft, beth oedd eich "
+"profiad\n"
+"o gael yr eiddo gwag yn ôl mewn defnydd?"
-#: web/confirm.cgi:143
+#: perllib/FixMyStreet/App/Controller/Contact.pm:109
+msgid "Illegal ID"
+msgstr "ID Anghyfreithlon"
+
+#: perllib/FixMyStreet/App/Controller/Alert.pm:105
+msgid "Illegal feed selection"
+msgstr "Dewis porthiant annilys"
+
+#: templates/web/default/around/display_location.html:61
+msgid "Include stale reports"
+msgstr "Cynnwys hen adroddiadau"
+
+#: templates/web/emptyhomes/tokens/confirm_problem.html:12
+#: templates/web/emptyhomes/tokens/confirm_problem.html:14
#, fuzzy
msgid ""
-"It is worth noting however that the process can sometimes be slow,\n"
+"It is worth noting however that the process can sometimes be slow, "
"especially if the property is in very poor repair or the owner is unwilling "
-"to\n"
-"act. In most cases it can take six months or more before you can expect to "
-"see\n"
-"anything change and sometimes there may be considerable barries to a "
-"property\n"
-"being brought back into use. This doesn&rsquo;t mean the council isn&rsquo;"
-"t\n"
-"doing anything. We encourage councils to update the website so you can\n"
-"see what is happening. It may be a long process, but you reporting your\n"
-"concerns about this property to the council is a valuable first step."
+"to act. In most cases it can take six months or more before you can expect "
+"to see anything change and sometimes there may be considerable barries to a "
+"property being brought back into use. This doesn&rsquo;t mean the council "
+"isn&rsquo;t doing anything. We encourage councils to update the website so "
+"you can see what is happening. It may be a long process, but you reporting "
+"your concerns about this property to the council is a valuable first step."
msgstr ""
"Mae'r rhan fwyaf o gynghorau'n eithaf da wrth adfer eiddo gwag i'w defnydd "
"eto. Er hynny,\n"
@@ -837,538 +992,469 @@ msgstr ""
"yn gwneud unrhyw beth. Rydym yn annog cynghorau i ddiweddaru'r wefan \n"
"fel y gallwch weld beth sy'n digwydd."
-#: web/confirm.cgi:151
-msgid ""
-"We may contact you periodically to ask if anything has changed\n"
-"with the property you reported."
-msgstr ""
-
-#: web/confirm.cgi:153
-msgid ""
-"Thank you for using ReportEmptyHomes.com. Your action is already helping\n"
-"to resolve the UK&rsquo;s empty homes crisis."
-msgstr ""
-"Diolch am ddefnyddio ReportEmptyHomes.com. Mae eich gweithred eisoes yn "
-"helpu i \n"
-"ddatrys argyfwng tai gwag y DU."
-
-#: web/confirm.cgi:155 web/confirm.cgi:161
-msgid "View your report"
-msgstr "Gweld eich adroddiad"
-
-#: web/confirm.cgi:157
-msgid ""
-"Thank you for reporting this empty property on ReportEmptyHomes.com.\n"
-"At present the report cannot be sent through to the council for this area. "
-"We\n"
-"are working with councils to link them into the system so that as many "
-"areas\n"
-"as possible will be covered."
-msgstr "Diolch am roi gwybod am eiddo gwag ar ReportEmptyHomes.com."
-
-#: web/confirm.cgi:166
-msgid "You have successfully confirmed your empty property"
-msgstr "Rydych wedi cadarnhau eich eiddo gwag yn llwyddiannus"
-
-#: web/confirm.cgi:167
-msgid " and <strong>we will now send it to the council</strong>"
-msgstr " a <strong>byddwn nawr yn ei anfon at y cyngor</strong>"
-
-#: web/confirm.cgi:168
-#, perl-format
-msgid ". You can <a href=\"%s\">view the empty property on this site</a>."
-msgstr ". Gallwch <a href=\"%s\">weld yr eiddo gwag ar y safle hwn</a>."
-
-#: web/confirm.cgi:192
-msgid ""
-"Thanks, glad to hear it's been returned to use! Could we just ask if you "
-"have ever reported an empty property to a council before?"
+#: templates/web/default/admin/council_contacts.html:23
+msgid "Last editor"
msgstr ""
-"Diolch, falch o glywed ei fod wedi cael ei adfer i'w ddefnyddio! Fyddai modd "
-"i ni'ch holi chi, a ydych erioed wedi rhoi gwybod i'r cyngor am eiddo gwag "
-"cyn hyn?"
-
-#: web/confirm.cgi:193 web/questionnaire.cgi:234 web/questionnaire.cgi:323
-#: web-admin/index.cgi:341 web-admin/index.cgi:342 web-admin/index.cgi:504
-#: web-admin/index.cgi:591 web-admin/index.cgi:609
-msgid "Yes"
-msgstr "Ydw"
-
-#: web/confirm.cgi:194 web/questionnaire.cgi:235 web/questionnaire.cgi:324
-#: web-admin/index.cgi:341 web-admin/index.cgi:342 web-admin/index.cgi:504
-#: web-admin/index.cgi:591 web-admin/index.cgi:609
-msgid "No"
-msgstr "Nac ydw"
-#: web/confirm.cgi:195 web/index.cgi:821
-msgid "Submit"
-msgstr "Anfon"
+#: templates/web/default/admin/report_edit.html:33
+#, fuzzy
+msgid "Last update:"
+msgstr "Anfonwch ddiweddariadau ataf i drwy'r e-bost"
-#: web/confirm.cgi:240
-#, perl-format
-msgid ""
-"Thank you &mdash; you can <a href=\"%s\">view your updated empty property</"
-"a> on the site."
+#: templates/web/default/admin/search_reports.html:49
+msgid "Last&nbsp;update:"
msgstr ""
-"Diolch &mdash; gallwch <a href=\"%s\">weld diweddariad eich eiddo gwag</a> "
-"ar y safle."
-
-#: web/contact.cgi:22
-msgid "Contact Us"
-msgstr "Cysylltwch â Ni"
-
-#: web/contact.cgi:41
-msgid "Please give your name"
-msgstr "Rhowch eich enw"
-#: web/contact.cgi:43
-msgid "Please give your email"
-msgstr "Rhowch eich cyfeiriad e-bost"
+#: templates/web/default/admin/council_contacts.html:11
+#, fuzzy
+msgid "List all reported empty properties"
+msgstr "Eiddo gwag yr adroddwyd amdanynt yn ddiweddar"
-#: web/contact.cgi:45
-msgid "Please give a valid email address"
-msgstr "Rhowch gyfeiriad e-bost dilys"
+#: templates/web/default/alert/choose.html:1
+#: templates/web/default/alert/choose.html:3
+#: templates/web/default/alert/index.html:1
+#: templates/web/default/alert/index.html:3
+#: templates/web/default/alert/list.html:1
+#: templates/web/default/alert/list.html:5
+#: templates/web/default/alert/updates.html:1
+#: templates/web/default/tokens/confirm_alert.html:1
+#: templates/web/default/tokens/confirm_alert.html:3
+msgid "Local RSS feeds and email alerts"
+msgstr "Porthiannau RSS a hysbysiadau e-bost lleol"
-#: web/contact.cgi:47
-msgid "Please give a subject"
-msgstr "Rhowch destun"
+#: templates/web/default/alert/list.html:1
+#: templates/web/default/alert/list.html:12
+#: templates/web/default/alert/list.html:14
+#: templates/web/default/alert/list.html:3
+#, fuzzy
+msgid "Local RSS feeds and email alerts for ‘%s’"
+msgstr "Porthiannau RSS a hysbysiadau e-bost lleol"
-#: web/contact.cgi:48
-msgid "Please write a message"
-msgstr "Ysgrifennwch neges"
+#: templates/web/default/footer.html:8 templates/web/emptyhomes/header.html:32
+#: templates/web/fiksgatami/footer.html:7
+msgid "Get local reports"
+msgstr "Gweld adroddiadau lleol"
-#: web/contact.cgi:49
-msgid "Illegal ID"
-msgstr "ID Anghyfreithlon"
+#: templates/web/default/index.html:60
+msgid "Locate the empty property on a map of the area"
+msgstr "Chwiliwch am leoliad yr eiddo gwag ar fap o'r ardal"
-#: web/contact.cgi:86
-msgid "Thanks for your feedback. We'll get back to you as soon as we can!"
-msgstr "Diolch am eich adborth. Byddwn yn eich ateb cyn gynted ag y gallwn!"
+#: templates/web/default/auth/general.html:43
+msgid "Log me in"
+msgstr ""
-#: web/contact.cgi:134
-msgid ""
-"We&rsquo;d love to hear what you think about this\n"
-"website. Just fill in the form. Please don&rsquo;t contact us about "
-"individual empty\n"
-"homes; use the box accessed from <a href=\"/\">the front page</a>."
+#: templates/web/default/auth/general.html:1
+#: templates/web/default/auth/general.html:3
+msgid "Login or create an account"
msgstr ""
-"Byddem wrth ein bodd yn clywed eich barn am y wefan hon.\n"
-"Dim ond llenwi'r ffurflen sydd angen i chi ei wneud. Peidiwch â defnyddio "
-"hwn i gysylltu â ni am dai gwag unigol;\n"
-"defnyddiwch y blwch sydd i'w gael ar y <a href=\"/\">dudalen flaen</a>."
-#: web/contact.cgi:141
-msgid ""
-"Please do <strong>not</strong> report empty properties through this form; "
-"messages go to\n"
-"the team behind reportemptyhomes.com, not a council. To report an empty "
-"property,\n"
-"please <a href=\"/\">go to the front page</a> and follow the instructions."
+#: templates/web/default/auth/logout.html:1
+msgid "Logout"
msgstr ""
-"<strong>Peidiwch</strong> ag adrodd am eiddo gwag drwy gyfrwng y ffurflen "
-"hon;\n"
-"bydd y negeseuon yn mynd at y tîm sydd y tu ôl i reportemptyhomes.com, nid "
-"at gyngor.\n"
-"I roi gwybod am eiddo gwag <a href=\"/\">ewch i'r dudalen flaen</a> a "
-"dilynwch y cyfarwyddiadau."
-#: web/contact.cgi:144
-#, perl-format
+#: perllib/FixMyStreet/Map/OSM.pm:43
msgid ""
-"We'd love to hear what you think about this site. Just fill in the form, or "
-"send an email to <a href='mailto:%s'>%s</a>:"
+"Map &copy; <a id=\"osm_link\" href=\"http://www.openstreetmap.org/"
+"\">OpenStreetMap</a> and contributors, <a href=\"http://creativecommons.org/"
+"licenses/by-sa/2.0/\">CC-BY-SA</a>"
msgstr ""
-"Byddai'n dda gennym glywed eich barn am y wefan hon. Llenwch y ffurflen, neu "
-"anfonwch neges e-bost at <a href='mailto:%s'>%s</a>:"
-
-#: web/contact.cgi:153 web/index.cgi:463
-msgid "There were problems with your report. Please see below."
-msgstr "Cafwyd anhawsterau gyda'ch adroddiad. Gweler isod."
-#: web/contact.cgi:158
-msgid "Contact the team"
-msgstr "Cysylltu â'r tîm"
-
-#: web/contact.cgi:196
+#: perllib/FixMyStreet/Map/OSM/StreetView.pm:27
+#: perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm:21
+#: perllib/FixMyStreet/Map/Tilma/Original/StreetView.pm:21
msgid ""
-"You are reporting the following update for being abusive, containing "
-"personal information, or similar:"
+"Map contains Ordnance Survey data &copy; Crown copyright and database right "
+"2010."
msgstr ""
-#: web/contact.cgi:207
+#: perllib/FixMyStreet/Map/FMS.pm:27
msgid ""
-"You are reporting the following empty property report for being abusive, "
-"containing personal information, or similar:"
+"Map contains Ordnance Survey data &copy; Crown copyright and database right "
+"2010. Microsoft"
msgstr ""
-"Rydych yn cwyno am yr adroddiad canlynol am eiddo gwag gan ei fod yn "
-"sarhaus, neu'n cynnwys gwybodaeth bersonol, neu debyg:"
-
-#: web/contact.cgi:234
-msgid "Your name:"
-msgstr "Eich enw:"
-
-#: web/contact.cgi:235
-msgid "Your&nbsp;email:"
-msgstr "Eich&nbsp;cyfeiriad e-bost:"
-#: web/contact.cgi:236 web/index.cgi:765 web-admin/index.cgi:623
-msgid "Subject:"
-msgstr "Pwnc:"
-
-#: web/contact.cgi:237
+#: templates/web/default/contact/index.html:90
msgid "Message:"
msgstr "Neges:"
-#: web/contact.cgi:238 web/index.cgi:1127
-msgid "Post"
-msgstr "Postio"
-
-#: web/faq.cgi:18
-msgid "Frequently Asked Questions"
-msgstr "Cwestiynau Cyffredin"
-
-#: web/flickr.cgi:45
-msgid ""
-"Thank you for trying to register for your Flickr photos. We seem to have a\n"
-"empty property ourselves though, so <a href=\"/contact\">please let us know "
-"what went on</a>\n"
-"and we'll look into it.\n"
-msgstr ""
-"Diolch am geisio cofrestru ar gyfer eich ffotograffau Flickr . Mae'n "
-"ymddangos bod gennym \n"
-"ein heiddo gwag ein hunain fodd bynnag, felly <a href=\"/contact\">rhowch "
-"wybod i ni beth ddigwyddodd</a>\n"
-"ac fe wnawn ni edrych iddo.\n"
+#: templates/web/default/report/display.html:38
+msgid "More empty properties nearby"
+msgstr "Mwy o eiddo gwag cyfagos"
-#: web/fun.cgi:17 web/fun.cgi:25
-msgid "Weird and Wonderful reports"
-msgstr "Adroddiadau rhyfeddol a rhagorol"
+#: templates/web/default/my/my.html:1
+#, fuzzy
+msgid "My Reports"
+msgstr "Adroddiadau"
-#: web/index.cgi:82
-msgid "Submitting your report"
-msgstr "Cyflwyno'ch adroddiad"
+#: templates/web/default/admin/list_updates.html:7
+#: templates/web/default/admin/search_reports.html:14
+#: templates/web/default/reports/index.html:10
+#: templates/web/emptyhomes/reports/index.html:10
+#: templates/web/fiksgatami/reports/index.html:9
+msgid "Name"
+msgstr "Enw"
-#: web/index.cgi:85
-msgid "Submitting your update"
-msgstr "Cyflwyno'ch diweddariad"
+#: templates/web/default/admin/report_edit.html:27
+#: templates/web/default/admin/update_edit.html:23
+#: templates/web/default/report/display.html:81
+#: templates/web/default/report/new/fill_in_details.html:130
+msgid "Name:"
+msgstr "Enw:"
-#: web/index.cgi:89 web/index.cgi:669
-msgid "Reporting an empty property"
-msgstr "Adrodd am eiddo gwag"
+#: templates/web/default/footer.html:4 templates/web/fiksgatami/footer.html:3
+msgid "Navigation"
+msgstr "Mordwyo "
-#: web/index.cgi:92
-msgid "Viewing an empty property"
-msgstr "Gweld eiddo gwag"
+#: templates/web/default/email_sent.html:24
+msgid "Nearly Done! Now check your email..."
+msgstr ""
-#: web/index.cgi:95
-msgid "Viewing a location"
-msgstr "Gweld lleoliad"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:291
+msgid "New category contact added"
+msgstr ""
-#: web/index.cgi:120
-msgid "Enter a nearby GB postcode, or street name and area"
-msgstr "Cofnodwch god post Prydeinig, neu enw stryd ac ardal gerllaw"
+#: db/alert_types.pl:18 db/alert_types.pl:22
+#, fuzzy
+msgid "New local empty properties on reportemptyhomes.com"
+msgstr "Eiddo gwag lleol diweddar, reportemptyhomes.com"
-#: web/index.cgi:134
-msgid "Report and view empty properties"
-msgstr "Adrodd am eiddo gwag a gweld y rhain"
+#: db/alert_types_eha.pl:12
+msgid "New local reports on reportemptyhomes.com"
+msgstr "Adroddiadau lleol newydd ar reportemptyhomes.com"
-#: web/index.cgi:135
-msgid " "
-msgstr " "
+#: templates/web/default/reports/council.html:43
+#: templates/web/default/reports/council.html:44
+#: templates/web/default/reports/index.html:11
+#: templates/web/emptyhomes/reports/index.html:11
+#: templates/web/fiksgatami/reports/index.html:10
+msgid "New empty property reports"
+msgstr "Adroddiadau newydd am eiddo gwag"
-#: web/index.cgi:159
+#: db/alert_types.pl:38
+#, fuzzy
msgid ""
-"Thanks for uploading your photo. We now need to locate your empty property, "
-"so please enter a nearby street name or postcode in the box below&nbsp;:"
+"New empty property reports for {{COUNCIL}} within {{WARD}} ward on "
+"reportemptyhomes.com"
msgstr ""
-"Diolch am lwytho'ch ffotograff i fyny. Nawr mae angen i ni ddod o hyd i "
-"leoliad eich eiddo gwag, felly rhowch enw stryd gerllaw neu god post yn y "
-"blwch isod&nbsp;:"
+"Adroddiadau newydd ar gyfer cyngor {{COUNCIL}}, yn ward {{WARD}} ar "
+"reportemptyhomes.com"
-#: web/index.cgi:177
-msgid "How to report an empty property"
-msgstr "Sut i roi gwybod am eiddo gwag"
+#: db/alert_types.pl:26 db/alert_types.pl:30
+#, fuzzy
+msgid "New empty property reports near {{POSTCODE}} on reportemptyhomes.com"
+msgstr "Adroddiadau newydd i gyngor {{COUNCIL}} ar reportemptyhomes.com"
-#: web/index.cgi:180
-msgid "Locate the empty property on a map of the area"
-msgstr "Chwiliwch am leoliad yr eiddo gwag ar fap o'r ardal"
+#: db/alert_types.pl:10
+#, fuzzy
+msgid "New empty property reports on reportemptyhomes.com"
+msgstr "Adroddiadau newydd ar reportemptyhomes.com"
-#: web/index.cgi:181
-msgid "Enter details of the empty property"
-msgstr "Rhowch fanylion yr eiddo gwag"
+#: db/alert_types.pl:34
+#, fuzzy
+msgid "New empty property reports to {{COUNCIL}} on reportemptyhomes.com"
+msgstr "Adroddiadau newydd i gyngor {{COUNCIL}} ar reportemptyhomes.com"
-#: web/index.cgi:182
+#: db/alert_types.pl:42
+#, fuzzy
msgid ""
-"The details will be sent directly to the right person in the local council "
-"for them to take action"
+"New empty property reports within {{NAME}}'s boundary on reportemptyhomes.com"
+msgstr "Adroddiadau newydd o fewn ffin {{NAME}} ar reportemptyhomes.com"
+
+#: db/alert_types_eha.pl:23
+msgid ""
+"New reports for {{COUNCIL}} within {{WARD}} ward on reportemptyhomes.com"
msgstr ""
-"Caiff y manylion eu hanfon yn uniongyrchol at yr unigolyn cywir yn y cyngor "
-"lleol er mwyn iddynt weithredu"
+"Adroddiadau newydd ar gyfer cyngor {{COUNCIL}}, yn ward {{WARD}} ar "
+"reportemptyhomes.com"
-#: web/index.cgi:197
-msgid "Photos of recent reports"
-msgstr "Ffotograffau o adroddiadau diweddar"
+#: db/alert_types_eha.pl:5
+msgid "New reports on reportemptyhomes.com"
+msgstr "Adroddiadau newydd ar reportemptyhomes.com"
-#: web/index.cgi:199
-msgid "Recently reported empty properties"
-msgstr "Eiddo gwag yr adroddwyd amdanynt yn ddiweddar"
+#: db/alert_types_eha.pl:16
+#, fuzzy
+msgid "New reports on reportemptyhomes.com near {{POSTCODE}}"
+msgstr "Adroddiadau newydd ar reportemptyhomes.com"
-#: web/index.cgi:232
-msgid "Please enter a message"
-msgstr "Ychwanegwch neges"
+#: db/alert_types_eha.pl:19
+msgid "New reports to {{COUNCIL}} on reportemptyhomes.com"
+msgstr "Adroddiadau newydd i gyngor {{COUNCIL}} ar reportemptyhomes.com"
-#: web/index.cgi:235 web/index.cgi:319
-msgid "Please enter your email"
-msgstr "Cofnodwch eich cyfeiriad e-bost"
+#: db/alert_types_eha.pl:27
+msgid "New reports within {{NAME}}'s boundary on reportemptyhomes.com"
+msgstr "Adroddiadau newydd o fewn ffin {{NAME}} ar reportemptyhomes.com"
-#: web/index.cgi:237 web/index.cgi:321
-msgid "Please enter a valid email"
-msgstr "Cofnodwch gyfeiriad e-bost dilys"
+#: templates/web/default/index.html:23
+msgid "New!"
+msgstr "Newydd!"
+
+#: templates/web/default/admin/council_contacts.html:32
+#: templates/web/default/admin/council_contacts.html:33
+#: templates/web/default/admin/council_edit.html:4
+#: templates/web/default/admin/list_updates.html:28
+#: templates/web/default/admin/report_edit.html:19
+#: templates/web/default/admin/report_edit.html:37
+#: templates/web/default/admin/search_reports.html:43
+#: templates/web/default/admin/update_edit.html:16
+#: templates/web/default/questionnaire/creator_fixed.html:16
+#: templates/web/default/questionnaire/index.html:109
+#: templates/web/default/questionnaire/index.html:70
+msgid "No"
+msgstr "Nac ydw"
-#: web/index.cgi:246 web/index.cgi:394
-#, perl-format
-msgid ""
-"That image doesn't appear to have uploaded correctly (%s), please try again."
+#: perllib/FixMyStreet/DB/Result/Problem.pm:187
+msgid "No council selected"
+msgstr "Ni ddewiswyd cyngor"
+
+#: templates/web/default/admin/council_list.html:32
+msgid "No edits have yet been made."
msgstr ""
-"Nid yw'n ymddangos bod y ddelwedd honno wedi llwytho i fyny'n gywir (%s), "
-"rhowch gynnig arni eto."
-#: web/index.cgi:268 web-admin/index.cgi:478
-msgid "Anonymous"
-msgstr "Di-enw"
+#: templates/web/default/admin/council_list.html:38
+msgid "No info at all"
+msgstr ""
-#: web/index.cgi:310
-msgid "No council selected"
-msgstr "Ni ddewiswyd cyngor"
+#: templates/web/default/around/around_map_list_items.html:15
+msgid "No empty properties found."
+msgstr "Ni ddaethpwyd o hyd i unrhyw eiddo gwag."
-#: web/index.cgi:311
-msgid "Please enter a subject"
-msgstr "Rhowch enw'r pwnc"
+#: templates/web/default/around/on_map_list_items.html:12
+msgid "No empty properties have been reported yet."
+msgstr "Ni roddwyd gwybod eto am unrhyw eiddo gwag."
-#: web/index.cgi:312
-msgid "Please enter some details"
-msgstr "Cofnodwch fanylion"
+#: templates/web/default/admin/council_list.html:5
+#: templates/web/default/admin/report_edit.html:16
+#, fuzzy
+msgid "None"
+msgstr "Nac ydw"
-#: web/index.cgi:314
-msgid "Please enter your name"
-msgstr "Cofnodwch eich enw"
+#: templates/web/default/admin/questionnaire.html:6
+#, fuzzy
+msgid "Not reported before"
+msgstr "Wedi adrodd o'r blaen"
-#: web/index.cgi:316
-msgid ""
-"Please enter your full name - if you do not wish your name to be shown on "
-"the site, untick the box"
-msgstr ""
-"Cofnodwch eich enw llawn - os nad ydych yn dymuno bod eich enw'n cael ei "
-"ddangos ar y safle, cliciwch i ddileu'r tic o'r bocs"
+#: templates/web/default/report/_main.html:9
+msgid "Not reported to council"
+msgstr "Ni roddwyd gwybod i'r cyngor am hyn"
-#: web/index.cgi:323 web/index.cgi:587 web/index.cgi:603
+#: templates/web/default/admin/council_contacts.html:24
+#: templates/web/default/admin/council_edit.html:46
#, fuzzy
-msgid "-- Pick a category --"
-msgstr "-- Dewiswch fath o eiddo --"
+msgid "Note"
+msgstr "Nac ydw"
-#: web/index.cgi:324 web/index.cgi:363 web/index.cgi:370
-msgid "Please choose a category"
-msgstr "Dewiswch gategori"
+#: templates/web/default/admin/council_contacts.html:73
+#: templates/web/default/admin/council_edit.html:31
+#, fuzzy
+msgid "Note:"
+msgstr "Nac ydw"
-#: web/index.cgi:326 web/index.cgi:580
-msgid "-- Pick a property type --"
-msgstr "-- Dewiswch fath o eiddo --"
+#: templates/web/default/report/display.html:33
+#: templates/web/default/report/updates.html:19
+msgid "Offensive? Unsuitable? Tell us"
+msgstr "Sarhaus? Anaddas? Rhowch wybod i ni"
-#: web/index.cgi:327
-msgid "Please choose a property type"
-msgstr "Dewiswch fath o eiddo"
+#: templates/web/default/reports/council.html:72
+#: templates/web/default/reports/council.html:73
+msgid "Old returned to use"
+msgstr "Hen wedi'i adfer i'w ddefnyddio"
+
+#: templates/web/default/reports/council.html:60
+#: templates/web/default/reports/council.html:61
+msgid "Old empty properties, state unknown"
+msgstr "Eiddo gwag hen, nid yw eu cyflwr yn hysbys"
-#: web/index.cgi:350
-msgid "That location is not part of that council"
-msgstr "Nid yw'r lleoliad hwnnw'n rhan o'r cyngor hwnnw"
+#: templates/web/default/reports/index.html:13
+#: templates/web/fiksgatami/reports/index.html:12
+msgid "Old empty properties,<br>state unknown"
+msgstr "Hen eiddo gwag,<br>cyflwr anhysbys"
-#: web/index.cgi:374
-msgid "We have details for that council"
-msgstr "Mae gennym fanylion ar gyfer y cyngor hwnnw"
+#: templates/web/default/reports/index.html:15
+#: templates/web/emptyhomes/reports/index.html:14
+#: templates/web/fiksgatami/reports/index.html:14
+msgid "Older returned to use"
+msgstr "Wedi'u hadfer i'w defnyddio ers amser"
-#: web/index.cgi:383
-msgid "Somehow, you only have one co-ordinate. Please try again."
-msgstr ""
-"Rhywffordd neu gilydd, dim ond un cyfesuryn sydd gennych. Rhowch gynnig arni "
-"eto."
+#: templates/web/default/reports/council.html:50
+#: templates/web/default/reports/council.html:52
+#: templates/web/default/reports/council.html:56
+#: templates/web/default/reports/council.html:57
+#: templates/web/default/reports/index.html:12
+#: templates/web/emptyhomes/reports/index.html:12
+#: templates/web/fiksgatami/reports/index.html:11
+msgid "Older empty property reports"
+msgstr "Adroddiadau hÅ·n o eiddo gwag"
-#: web/index.cgi:385
-msgid "You haven't specified any sort of co-ordinates. Please try again."
+#: templates/web/default/admin/report_edit.html:22
+#: templates/web/default/admin/update_edit.html:19
+msgid "Open"
msgstr ""
-"Nid ydych wedi pennu unrhyw fath o gyfesurynnau. Rhowch gynnig arni eto."
-#: web/index.cgi:424
-#, perl-format
-msgid ""
-"You have successfully confirmed your report and you can now <a href=\"%s"
-"\">view it on the site</a>."
-msgstr ""
-"Rydych wedi cadarnhau eich adroddiad yn llwyddiannus ac nawr gallwch <a href="
-"\"%s\">weld eich adroddiad ar y safle</a>."
+#: templates/web/default/alert/list.html:88
+msgid "Or empty properties reported to:"
+msgstr "Neu eiddo gwag y rhoddwyd gwybod amdanynt i:"
-#: web/index.cgi:566
+#: templates/web/default/alert/list.html:63
msgid ""
-"That spot does not appear to be covered by a council.\n"
-"If you have tried to report an issue past the shoreline, for example,\n"
-"please specify the closest point on land."
+"Or you can subscribe to an alert based upon what ward or council you&rsquo;"
+"re in:"
msgstr ""
-"Nid yw'n ymddangos bod cyngor yn gyfrifol am y lleoliad hwnnw.\n"
-"Os ydych wedi ceisio adrodd am broblem oddi ar y lân, er enghraifft,\n"
-"dylech nodi'r pwynt agosaf ar y tir."
+"Neu, gallwch danysgrifio am hysbysiad yn ôl pa ward neu gyngor yr ydych yn "
+"byw ynddo:"
-#: web/index.cgi:580
-msgid "Empty house or bungalow"
-msgstr "TÅ· neu fyngalo gwag"
+#: bin/send-reports:165 bin/send-reports:174
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:492
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:501
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:856
+#: perllib/FixMyStreet/DB/Result/Problem.pm:316
+#: perllib/FixMyStreet/DB/Result/Problem.pm:326
+#: perllib/FixMyStreet/DB/Result/Problem.pm:336
+#: perllib/FixMyStreet/DB/Result/Problem.pm:348
+msgid "Other"
+msgstr "Arall"
-#: web/index.cgi:581
-msgid "Empty flat or maisonette"
-msgstr "Fflat neu fflat deulawr gwag"
+#: templates/web/default/errors/page_error_410_gone.html:1
+#: templates/web/default/errors/page_error_410_gone.html:3
+msgid "Page Gone"
+msgstr ""
-#: web/index.cgi:581
-msgid "Whole block of empty flats"
-msgstr "Bloc cyfan o fflatiau gwag"
+#: templates/web/default/errors/page_error_404_not_found.html:1
+#: templates/web/default/errors/page_error_404_not_found.html:3
+msgid "Page Not Found"
+msgstr ""
-#: web/index.cgi:582
-msgid "Empty office or other commercial"
-msgstr "Swyddfa wag neu fan masnachol gwag arall"
+#: templates/web/default/admin/report_edit.html:22
+msgid "Partial"
+msgstr ""
-#: web/index.cgi:582
-msgid "Empty pub or bar"
-msgstr "Tafarn neu far gwag"
+#: templates/web/default/auth/change_password.html:26
+#: templates/web/default/auth/general.html:33
+msgid "Password:"
+msgstr ""
-#: web/index.cgi:583
-msgid "Empty public building - school, hospital, etc."
-msgstr "Adeilad cyhoeddus gwag - ysgol, ysbyty, ac ati."
+#: bin/send-reports:68 templates/web/default/admin/report_edit.html:29
+#: templates/web/default/report/new/fill_in_details.html:158
+msgid "Phone:"
+msgstr "Rhif ffôn:"
-#: web/index.cgi:584
-msgid "Property type:"
-msgstr "Math o eiddo:"
+#: templates/web/default/questionnaire/index.html:97
+#: templates/web/default/report/display.html:125
+#: templates/web/default/report/new/fill_in_details.html:120
+msgid "Photo:"
+msgstr "Ffotograff:"
-#: web/index.cgi:588 web/index.cgi:604 web-admin/index.cgi:629
-msgid "Category:"
-msgstr "Categori:"
+#: templates/web/default/alert/list.html:29
+msgid "Photos of recent nearby reports"
+msgstr "Ffotograffau o adroddiadau cyfagos diweddar"
-#: web/index.cgi:663
-msgid ""
-"Please note your report has <strong>not yet been sent</strong>. Choose a "
-"category and add further information below, then submit."
+#: templates/web/default/index.html:78
+msgid "Photos of recent reports"
+msgstr "Ffotograffau o adroddiadau diweddar"
+
+#: templates/web/default/report/new/notes.html:6
+msgid "Please be polite, concise and to the point."
msgstr ""
-#: web/index.cgi:666
-msgid ""
-"You have located the empty property at the point marked with a purple pin on "
-"the map.\n"
-"If this is not the correct location, simply click on the map again. "
+#: templates/web/default/auth/change_password.html:12
+#: templates/web/default/auth/change_password.html:17
+msgid "Please check the passwords and try again"
msgstr ""
-"Rydych wedi gosod lleoliad yr eiddo gwag wrth y pwynt sy'n cael ei ddangos "
-"gan y pin porffor ar y map.\n"
-"Os nad dyma'r lleoliad cywir, cliciwch ar y map eto. "
-#: web/index.cgi:672 web/index.cgi:697 web/index.cgi:699 web/index.cgi:712
+#: templates/web/default/auth/token.html:17
#, fuzzy
-msgid " or "
-msgstr "neu"
+msgid "Please check your email"
+msgstr "Rhowch eich cyfeiriad e-bost"
-#: web/index.cgi:674
-#, perl-format
-msgid ""
-"All the information you provide here will be sent to <strong>%s</strong>.\n"
-"On the site, we will show the subject and details of the empty property, "
-"plus your\n"
-"name if you give us permission."
-msgstr ""
-"Bydd yr holl wybodaeth a ddarperir gennych yma'n cael ei hanfon at <strong>%"
-"s</strong>.\n"
-"Ar y safle, byddwn yn dangos pwnc a manylion yr eiddo gwag,\n"
-"ynghyd â'ch enw os ydych yn rhoi caniatâd i ni."
+#: templates/web/default/auth/general.html:14
+#: templates/web/default/auth/general.html:8
+#, fuzzy
+msgid "Please check your email address is correct"
+msgstr "Rhowch gyfeiriad e-bost dilys"
+
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:640
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:660
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:676
+#: perllib/FixMyStreet/DB/Result/Problem.pm:206
+msgid "Please choose a category"
+msgstr "Dewiswch gategori"
+
+#: perllib/FixMyStreet/DB/Result/Problem.pm:212
+msgid "Please choose a property type"
+msgstr "Dewiswch fath o eiddo"
-#: web/index.cgi:678
-#, fuzzy, perl-format
+#: templates/web/default/contact/blurb.html:2
msgid ""
-"All the information you\n"
-" provide here will be sent to <strong>%s</strong> or a relevant\n"
-" local body such as TfL, via the London Report-It system. The\n"
-" subject and details of the empty property will be public, plus "
-"your name\n"
-" if you give us permission."
+"Please do <strong>not</strong> report empty properties through this form; "
+"messages go to\n"
+"the team behind reportemptyhomes.com, not a council. To report an empty "
+"property,\n"
+"please <a href=\"/\">go to the front page</a> and follow the instructions."
msgstr ""
-"Bydd yr holl wybodaeth a ddarperir gennych yma'n cael ei hanfon at <strong>%"
-"s</strong>.\n"
-"Ar y safle, byddwn yn dangos pwnc a manylion yr eiddo gwag,\n"
-"ynghyd â'ch enw os ydych yn rhoi caniatâd i ni."
+"<strong>Peidiwch</strong> ag adrodd am eiddo gwag drwy gyfrwng y ffurflen "
+"hon;\n"
+"bydd y negeseuon yn mynd at y tîm sydd y tu ôl i reportemptyhomes.com, nid "
+"at gyngor.\n"
+"I roi gwybod am eiddo gwag <a href=\"/\">ewch i'r dudalen flaen</a> a "
+"dilynwch y cyfarwyddiadau."
-#: web/index.cgi:684
-#, perl-format
+#: templates/web/default/report/new/notes.html:7
msgid ""
-"All the information you provide here will be sent to <strong>%s</strong>.\n"
-"The subject and details of the empty property will be public, plus your\n"
-"name if you give us permission."
+"Please do not be abusive &mdash; abusing your council devalues the service "
+"for all users."
msgstr ""
-#: web/index.cgi:698
-msgid "All the information you provide here will be sent to"
-msgstr ""
+#: perllib/FixMyStreet/DB/Result/Comment.pm:108
+msgid "Please enter a message"
+msgstr "Ychwanegwch neges"
-#: web/index.cgi:701
-msgid ""
-"The subject and details of the empty property will be public, plus your name "
-"if you give us permission."
-msgstr ""
+#: templates/web/default/auth/change_password.html:12
+#: templates/web/default/auth/change_password.html:15
+#, fuzzy
+msgid "Please enter a password"
+msgstr "Ychwanegwch neges"
-#: web/index.cgi:703
-msgid ""
-"We do <strong>not</strong> yet have details for the other council that "
-"covers this location."
-msgid_plural ""
-"We do <strong>not</strong> yet have details for the other councils that "
-"cover this location."
-msgstr[0] ""
-msgstr[1] ""
+#: perllib/FixMyStreet/App/Controller/Contact.pm:95
+#: perllib/FixMyStreet/DB/Result/Problem.pm:181
+msgid "Please enter a subject"
+msgstr "Rhowch enw'r pwnc"
-#: web/index.cgi:707 web/index.cgi:722
-#, perl-format
-msgid ""
-"You can help us by finding a contact email address for local empty "
-"properties for %s and emailing it to us at <a href='mailto:%s'>%s</a>."
-msgstr ""
+#: perllib/FixMyStreet/DB/Result/User.pm:91
+msgid "Please enter a valid email"
+msgstr "Cofnodwch gyfeiriad e-bost dilys"
-#: web/index.cgi:717 web/index.cgi:725
-msgid "We do not yet have details for the council that covers this location."
-msgid_plural ""
-"We do not yet have details for the councils that cover this location."
-msgstr[0] ""
-msgstr[1] ""
+#: perllib/FixMyStreet/App/Controller/Alert.pm:123
+#: perllib/FixMyStreet/App/Controller/Contact.pm:105
+msgid "Please enter a valid email address"
+msgstr "Cofnodwch gyfeiriad e-bost dilys"
-#: web/index.cgi:721
-msgid ""
-"If you submit an empty property here the subject and details of the empty "
-"property will be public, but the empty property will <strong>not</strong> be "
-"reported to the council."
-msgstr ""
+#: perllib/FixMyStreet/DB/Result/Problem.pm:184
+msgid "Please enter some details"
+msgstr "Cofnodwch fanylion"
-#: web/index.cgi:727
-#, fuzzy
-msgid ""
-"If you submit a report here it will be left on the site, but not reported to "
-"the council &ndash; please still leave your report, so that we can show to "
-"the council the activity in their area."
-msgstr ""
-"<p>Nid oes gennym fanylion eto ar gyfer y cyngor sy'n gyfrifol am \n"
-"y lleoliad hwn. Os ydych yn cyflwyno adroddiad yma, bydd yn cael ei adael ar "
-"y safle, ond\n"
-"ni fydd yn cael ei adrodd i'r cyngor &ndash; gadewch eich adroddiad o hyd, "
-"fel y gallwn\n"
-"ddangos i'r cyngor y gweithgarwch yn ei ardal."
+#: perllib/FixMyStreet/App/Controller/Contact.pm:94
+#: perllib/FixMyStreet/DB/Result/User.pm:88
+#: templates/web/default/auth/general.html:13
+#: templates/web/default/auth/general.html:8
+msgid "Please enter your email"
+msgstr "Cofnodwch eich cyfeiriad e-bost"
-#: web/index.cgi:733
+#: perllib/FixMyStreet/DB/Result/Problem.pm:199
+#: perllib/FixMyStreet/DB/Result/User.pm:83
msgid ""
-"Please fill in the form below with details of the empty property,\n"
-"and describe the location as precisely as possible in the details box."
+"Please enter your full name - if you do not wish your name to be shown on "
+"the site, untick the box"
msgstr ""
-"Llenwch y ffurflen isod gyda manylion yr eiddo gwag,\n"
-"a disgrifiwch y lleoliad mor fanwl ag y bo modd yn y blwch manylion."
+"Cofnodwch eich enw llawn - os nad ydych yn dymuno bod eich enw'n cael ei "
+"ddangos ar y safle, cliciwch i ddileu'r tic o'r bocs"
+
+#: perllib/FixMyStreet/App/Controller/Contact.pm:93
+#: perllib/FixMyStreet/DB/Result/Problem.pm:192
+#: perllib/FixMyStreet/DB/Result/User.pm:76
+msgid "Please enter your name"
+msgstr "Cofnodwch eich enw"
-#: web/index.cgi:742
+#: templates/web/emptyhomes/report/new/fill_in_details_text.html:1
+#, fuzzy
msgid ""
"Please fill in details of the empty property below, saying what type of\n"
"property it is e.g. an empty home, block of flats, office etc. Tell us\n"
@@ -1378,7 +1464,7 @@ msgid ""
"concise\n"
"and to the point; writing your message entirely in block capitals makes it "
"hard\n"
-"to read, as does a lack of punctuation.\n"
+"to read, as does a lack of punctuation."
msgstr ""
"Llenwch fanylion yr eiddo gwag isod, gan ddweud pa fath o eiddo ydyw\n"
"e.e. tÅ· gwag, bloc o fflatiau, swyddfa ac ati. Dywedwch rywbeth wrthym\n"
@@ -1387,7 +1473,13 @@ msgstr ""
"ac i'r pwynt; mae ysgrifennu eich neges yn gyfan gwbl mewn priflythrennau\n"
"yn ei gwneud hi'n anodd ei darllen, fel y mae diffyg atalnodi.\n"
-#: web/index.cgi:750
+#: templates/web/default/report/new/fill_in_details_text.html:1
+#: templates/web/default/report/new/fill_in_details_text.html:8
+msgid "Please fill in details of the empty property below."
+msgstr "Llenwch fanylion yr eiddo gwag isod."
+
+#: templates/web/default/report/new/fill_in_details_text.html:1
+#: templates/web/default/report/new/fill_in_details_text.html:3
msgid ""
"Please fill in details of the empty property below. The council won't be "
"able\n"
@@ -1398,718 +1490,732 @@ msgid ""
"photo of the empty property if you have one), etc."
msgstr ""
-#: web/index.cgi:755
-msgid "Please fill in details of the empty property below."
-msgstr "Llenwch fanylion yr eiddo gwag isod."
-
-#: web/index.cgi:764
-msgid "Empty property details form"
-msgstr "Ffurflen manylion eiddo gwag"
-
-#: web/index.cgi:766 web-admin/index.cgi:624
-msgid "Details:"
-msgstr "Manylion:"
-
-#: web/index.cgi:767 web/index.cgi:1143 web/questionnaire.cgi:308
-msgid "Photo:"
-msgstr "Ffotograff:"
-
-#: web/index.cgi:768 web/index.cgi:1124 web-admin/index.cgi:630
-#: web-admin/index.cgi:750
-msgid "Name:"
-msgstr "Enw:"
-
-#: web/index.cgi:770 bin/send-reports:94 web-admin/index.cgi:632
-msgid "Phone:"
-msgstr "Rhif ffôn:"
-
-#: web/index.cgi:771 web/index.cgi:1131
-msgid "(optional)"
-msgstr "(dewisol)"
-
-#: web/index.cgi:773
-msgid "Can we show your name on the site?"
-msgstr "Allwn ni ddangos eich enw chi ar y safle?"
-
-#: web/index.cgi:775
-msgid "Can we show your name publicly?"
+#: templates/web/default/report/new/fill_in_details.html:49
+#, fuzzy
+msgid ""
+"Please fill in the form below with details of the empty property, and "
+"describe the location as precisely as possible in the details box."
msgstr ""
+"Llenwch y ffurflen isod gyda manylion yr eiddo gwag,\n"
+"a disgrifiwch y lleoliad mor fanwl ag y bo modd yn y blwch manylion."
-#: web/index.cgi:777
-msgid "(we never show your email address or phone number)"
-msgstr "(nid ydym byth yn dangos eich cyfeiriad e-bost na'ch rhif ffôn)"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:239
+msgid "Please indicate whether you'd like to receive another questionnaire"
+msgstr "Nodwch a fyddech chi'n hoffi derbyn holiadur arall"
-#: web/index.cgi:804
-msgid "Please note:"
+#: templates/web/default/report/display.html:69
+msgid ""
+"Please note that updates are not sent to the council. If you leave your name "
+"it will be public. Your information will only be used in accordance with our "
+"<a href=\"/faq#privacy\">privacy policy</a>"
msgstr ""
-#: web/index.cgi:806
+#: templates/web/default/report/new/fill_in_details.html:34
msgid ""
-"We will only use your personal information in accordance with our <a href=\"/"
-"faq#privacy\">privacy policy.</a>"
+"Please note your report has <strong>not yet been sent</strong>. Choose a "
+"category and add further information below, then submit."
msgstr ""
-#: web/index.cgi:807
-msgid "Please be polite, concise and to the point."
+#: templates/web/default/report/new/notes.html:1
+msgid "Please note:"
msgstr ""
-#: web/index.cgi:808
-msgid ""
-"Please do not be abusive &mdash; abusing your council devalues the service "
-"for all users."
-msgstr ""
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:242
+msgid "Please provide some explanation as to why you're reopening this report"
+msgstr "Rhowch ychydig o esboniad pam rydych yn ailagor yr adroddiad hwn"
-#: web/index.cgi:809
-msgid ""
-"Writing your message entirely in block capitals makes it hard to read, as "
-"does a lack of punctuation."
-msgstr ""
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:249
+msgid "Please provide some text as well as a photo"
+msgstr "Rhowch rywfaint o destun yn ogystal â ffotograff"
-#: web/index.cgi:810
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:119
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:235
msgid ""
-"Remember that reportemptyhomes.com is primarily for reporting physical empty "
-"properties that can be returned to use. If your empty property is not "
-"appropriate for submission via this site remember that you can contact your "
-"council directly using their own website."
+"Please say whether you've ever reported an empty property to your council "
+"before"
msgstr ""
+"Rhowch wybod a ydych chi wedi rhoi gwybod i'ch cyngor am eiddo gwag erioed "
+"o'r blaen"
-#: web/index.cgi:812
-msgid ""
-"reportemptyhomes.com and the Guardian are providing this service in "
-"partnership in <a href=\"/faq#privacy\">certain cities</a>. In those cities, "
-"both have access to any information submitted, including names and email "
-"addresses, and will use it only to ensure the smooth running of the service, "
-"in accordance with their privacy policies."
-msgstr ""
+#: perllib/FixMyStreet/App/Controller/Alert.pm:85
+msgid "Please select the feed you want"
+msgstr "Dewiswch y porthiant rydych chi eisiau ei gael"
-#: web/index.cgi:916
-msgid "Hide stale reports"
-msgstr "Cuddio hen adroddiadau "
+#: perllib/FixMyStreet/App/Controller/Alert.pm:125
+msgid "Please select the type of alert you want"
+msgstr "Dewiswch y math o hysbysiad rydych chi eisiau ei gael"
-#: web/index.cgi:919
-msgid "Include stale reports"
-msgstr "Cynnwys hen adroddiadau"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:231
+msgid "Please state whether or not the empty property has been returned to use"
+msgstr ""
+"Cofnodwch a yw'r eiddo gwag wedi cael ei adfer i'w ddefnyddio, ai peidio"
-#: web/index.cgi:938
-msgid "No empty properties have been reported yet."
-msgstr "Ni roddwyd gwybod eto am unrhyw eiddo gwag."
+#: templates/web/default/questionnaire/index.html:52
+msgid "Please take a look at the updates that have been left."
+msgstr "Ewch i fwrw golwg ar y diweddariadau sydd wedi cael eu gadael."
-#: web/index.cgi:954
-msgid "No empty properties found."
-msgstr "Ni ddaethpwyd o hyd i unrhyw eiddo gwag."
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:729
+msgid "Please upload a JPEG image only"
+msgstr "Dim ond llun JPEG y dylech lwytho i fyny."
-#: web/index.cgi:959
-msgid "Show pins"
-msgstr "Dangos pinnau"
+#: perllib/FixMyStreet/App/Controller/Contact.pm:96
+msgid "Please write a message"
+msgstr "Ysgrifennwch neges"
-#: web/index.cgi:963
-msgid "Hide pins"
-msgstr "Cuddio pinnau"
+#: templates/web/default/contact/index.html:93
+#: templates/web/default/report/display.html:138
+msgid "Post"
+msgstr "Postio"
-#: web/index.cgi:1004
-msgid "Email me new local empty properties"
-msgstr "Anfonwch fanylion eiddo gwag lleol newydd ataf i drwy'r e-bost"
+#: templates/web/default/report/updates.html:8
+msgid "Posted anonymously at %s"
+msgstr "Cofnodwyd yn ddi-enw am %s"
-#: web/index.cgi:1006
-msgid "RSS feed of recent local empty properties"
-msgstr "Porthiant RSS o eiddo gwag lleol diweddar"
+#: templates/web/default/report/updates.html:10
+msgid "Posted by %s at %s"
+msgstr "Cofnodwyd gan %s am %s"
-#: web/index.cgi:1009
-msgid "Empty properties in this area"
-msgstr "Eiddo gwag yn yr ardal hon"
+#: perllib/FixMyStreet/Map/Tilma/Original.pm:90
+#: templates/web/default/maps/openlayers.html:81
+#: templates/web/default/maps/tilma/original.html:62
+msgid "Empty property"
+msgstr "Eiddo gwag"
-#: web/index.cgi:1010
-msgid "Reports on and around the map"
-msgstr "Adroddiadau ar ac yng nghyffiniau'r map"
+#: templates/web/default/admin/timeline.html:24
+#, fuzzy
+msgid "Empty property %d created"
+msgstr "Eiddo gwag"
-#: web/index.cgi:1011
-#, perl-format
-msgid "Closest nearby empty properties <small>(within&nbsp;%skm)</small>"
-msgstr "Yr eiddo gwag cyfagos agosaf <small>(within&nbsp;%skm)</small>"
+#: templates/web/default/admin/timeline.html:26
+#, fuzzy
+msgid "Empty property %s confirmed"
+msgstr "Ffurflen manylion eiddo gwag"
-#: web/index.cgi:1015
-msgid ""
-"To report an empty property, simply\n"
-" <strong>click on the map</strong> at the correct location."
-msgstr ""
-"Er mwyn rhoi gwybod am eiddo gwag\n"
-" <strong>cliciwch ar y map</strong> wrth y lleoliad cywir."
+#: templates/web/default/admin/timeline.html:28
+#, fuzzy
+msgid "Empty property %s sent to council %s"
+msgstr "Eiddo gwag yn %s"
-#: web/index.cgi:1017
-#, perl-format
-msgid ""
-"<small>If you cannot see the map, <a href='%s' rel='nofollow'>skip this\n"
-" step</a>.</small>"
-msgstr ""
-"<small>Os na allwch weld y map, <a href='%s' rel='nofollow'>ewch heibio\n"
-" i'r cam hwn</a>.</small>"
+#: templates/web/default/admin/index.html:27
+#, fuzzy
+msgid "Empty property breakdown by state"
+msgstr "ni fydd eich eiddo gwag yn cael ei bostio"
-#: web/index.cgi:1022
-msgid "Recent local empty properties, reportemptyhomes.com"
-msgstr "Eiddo gwag lleol diweddar, reportemptyhomes.com"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:745
+#, fuzzy
+msgid "Empty property marked as open."
+msgstr "Ffurflen manylion eiddo gwag"
-#: web/index.cgi:1035
-msgid "There were problems with your update. Please see below."
-msgstr "Cafwyd anhawsterau gyda'ch diweddariad. Gweler isod"
+#: templates/web/default/around/display_location.html:73
+msgid "Empty properties in this area"
+msgstr "Eiddo gwag yn yr ardal hon"
-#: web/index.cgi:1056 web/index.cgi:1058
-msgid "Unknown empty property ID"
-msgstr "ID eiddo gwag anhysbys"
+#: db/alert_types.pl:14
+#, fuzzy
+msgid ""
+"Empty properties recently reported returned to use on reportemptyhomes.com"
+msgstr ""
+"Eiddo y cafwyd adroddiadau diweddar ar reportemptyhomes.com eu bod wedi cael "
+"eu hadfer i'w defnyddio"
-#: web/index.cgi:1059
-msgid "That report has been removed from reportemptyhomes.com."
-msgstr "Mae'r adroddiad hwnnw wedi cael ei dynnu oddi ar reportemptyhomes.com"
+#: templates/web/default/alert/list.html:52
+#, fuzzy
+msgid "Empty properties within %.1fkm of this location"
+msgstr "Eiddo gwag o fewn %skm o'r lleoliad hwn"
-#: web/index.cgi:1079
-msgid "This empty property is old and of unknown status."
-msgstr "Mae'r eiddo gwag hwn yn hen ac nid yw ei statws yn hysbys."
+#: perllib/FixMyStreet/Cobrand/Default.pm:694
+#: perllib/FixMyStreet/Cobrand/EmptyHomes.pm:161
+#: perllib/FixMyStreet/Cobrand/FiksGataMi.pm:123
+msgid "Empty properties within %s"
+msgstr "Eiddo gwag yn %s"
-#: web/index.cgi:1082 web/index.cgi:1123
-msgid "This empty property has been returned to use"
-msgstr "Mae'r eiddo gwag hwn wedi cael ei adfer i'w ddefnyddio"
+#: perllib/FixMyStreet/Cobrand/Default.pm:703
+#: perllib/FixMyStreet/Cobrand/EmptyHomes.pm:169
+msgid "Empty properties within %s ward"
+msgstr "Eiddo gwag yn ward %s"
-#: web/index.cgi:1107
-msgid "More empty properties nearby"
-msgstr "Mwy o eiddo gwag cyfagos"
+#: templates/web/default/reports/council.html:11
+#: templates/web/default/reports/council.html:14
+msgid "Empty properties within %s, reportemptyhomes.com"
+msgstr "Eiddo gwag o fewn %s, reportemptyhomes.com"
-#: web/index.cgi:1110
-msgid "Email me updates"
-msgstr "Anfonwch ddiweddariadau ataf i drwy'r e-bost"
+#: templates/web/default/alert/list.html:69
+msgid "Empty properties within the boundary of:"
+msgstr "Eiddo gwag o fewn ffiniau:"
-#: web/index.cgi:1113
-msgid "Receive email when updates are left on this empty property"
+#: db/alert_types_eha.pl:8
+msgid "Properties recently reported as put back to use on reportemptyhomes.com"
msgstr ""
-"Hoffwn dderbyn neges e-bost pan gaiff diweddariadau eu rhoi am yr eiddo gwag "
-"hwn"
+"Eiddo y cafwyd adroddiadau diweddar ar reportemptyhomes.com eu bod wedi cael "
+"eu hadfer i'w defnyddio"
-#: web/index.cgi:1118
-msgid "RSS feed of updates to this empty property"
-msgstr "Porthiant RSS o ddiweddariadau i'r eiddo gwag hwn"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:469
+msgid "Property type:"
+msgstr "Math o eiddo:"
-#: web/index.cgi:1119
+#: templates/web/default/report/display.html:64
msgid "Provide an update"
msgstr "Rhowch ddiweddariad"
-#: web/index.cgi:1125
-msgid "Update:"
-msgstr "Diweddariad:"
+#: templates/web/default/questionnaire/index.html:0
+#: templates/web/default/questionnaire/index.html:14
+#: templates/web/default/questionnaire/index.html:4
+msgid "Questionnaire"
+msgstr "Holiadur"
-#: web/index.cgi:1126
-msgid "Alert me to future updates"
-msgstr "Rhowch wybod i mi am ddiweddariadau yn y dyfodol"
+#: templates/web/default/admin/timeline.html:32
+#, fuzzy
+msgid "Questionnaire %d answered for empty property %d, %s to %s"
+msgstr "Llenwyd yr holiadur gan yr unigolyn a roddodd wybod am yr eiddo gwag"
-#: web/index.cgi:1134
-msgid ""
-"Please note that updates are not sent to the council. If you leave your name "
-"it will be public. Your information will only be used in accordance with our "
-"<a href=\"/faq#privacy\">privacy policy</a>"
-msgstr ""
+#: templates/web/default/admin/timeline.html:30
+#, fuzzy
+msgid "Questionnaire %d sent for empty property %d"
+msgstr "Llenwyd yr holiadur gan yr unigolyn a roddodd wybod am yr eiddo gwag"
-#: web/index.cgi:1154
-msgid "Updates to this empty property, reportemptyhomes.com"
-msgstr "Diweddariadau i'r eiddo gwag hwn, reportemptyhomes.com"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:185
+msgid "Questionnaire filled in by empty property reporter"
+msgstr "Llenwyd yr holiadur gan yr unigolyn a roddodd wybod am yr eiddo gwag"
-#: web/questionnaire.cgi:29 web/questionnaire.cgi:80 web/questionnaire.cgi:213
-#: web/questionnaire.cgi:233
-msgid "Questionnaire"
-msgstr "Holiadur"
+#: templates/web/default/alert/list.html:54
+#: templates/web/default/around/display_location.html:1
+#: templates/web/default/around/display_location.html:3
+#: templates/web/default/report/display.html:55
+#: templates/web/default/reports/council.html:17
+msgid "RSS feed"
+msgstr "Porthiant RSS"
-#: web/questionnaire.cgi:41
-msgid ""
-"I'm afraid we couldn't validate that token. If you've copied the URL from an "
-"email, please check that you copied it exactly.\n"
-msgstr ""
-"Yn anffodus, ni fu modd i ni ddilysu'r dynodiad hwnnw. Os ydych wedi copïo'r "
-"URL o neges e-bost, gwnewch yn siwr eich bod wedi'i gopïo'n union.\n"
+#: perllib/FixMyStreet/Cobrand/Default.pm:732
+#: perllib/FixMyStreet/Cobrand/Default.pm:746
+#, fuzzy
+msgid "RSS feed for %s"
+msgstr "Porthiant RSS %s"
-#: web/questionnaire.cgi:48
-#, perl-format
-msgid ""
-"You have already answered this questionnaire. If you have a question, please "
-"<a href='%s'>get in touch</a>, or <a href='%s'>view your empty property</"
-"a>.\n"
-msgstr ""
-"Rydych eisoes wedi ateb yr holiadur hwn. Os oes gennych gwestiwn, <a href='%"
-"s'>cysylltwch â ni</a>, neu ewch i <a href='%s'>weld eich eiddo gwag</a>.\n"
+#: perllib/FixMyStreet/Cobrand/Default.pm:739
+#: perllib/FixMyStreet/Cobrand/Default.pm:753
+#, fuzzy
+msgid "RSS feed for %s ward, %s"
+msgstr "Porthiant RSS %s"
-#: web/questionnaire.cgi:53
-msgid "I'm afraid we couldn't locate your empty property in the database.\n"
-msgstr ""
-"Yn anffodus ni fu modd i ni ddod o hyd i'ch eiddo gwag yn y gronfa ddata.\n"
+#: perllib/FixMyStreet/Cobrand/Default.pm:762
+#: perllib/FixMyStreet/Cobrand/Default.pm:776
+#: perllib/FixMyStreet/Cobrand/FiksGataMi.pm:139
+#: perllib/FixMyStreet/Cobrand/FiksGataMi.pm:147
+#: perllib/FixMyStreet/Cobrand/FiksGataMi.pm:157
+#: perllib/FixMyStreet/Cobrand/FiksGataMi.pm:165
+msgid "RSS feed of %s"
+msgstr "Porthiant RSS %s"
-#: web/questionnaire.cgi:92
-msgid "Please state whether or not the empty property has been returned to use"
-msgstr ""
-"Cofnodwch a yw'r eiddo gwag wedi cael ei adfer i'w ddefnyddio, ai peidio"
+#: perllib/FixMyStreet/Cobrand/Default.pm:768
+#: perllib/FixMyStreet/Cobrand/Default.pm:782
+#, fuzzy
+msgid "RSS feed of %s, within %s ward"
+msgstr "Porthiant RSS %s"
-#: web/questionnaire.cgi:95
-msgid ""
-"Please say whether you've ever reported an empty property to your council "
-"before"
-msgstr ""
-"Rhowch wybod a ydych chi wedi rhoi gwybod i'ch cyngor am eiddo gwag erioed "
-"o'r blaen"
+#: templates/web/default/alert/list.html:54
+msgid "RSS feed of nearby empty properties"
+msgstr "Porthiant RSS o eiddo gwag sydd gerllaw"
-#: web/questionnaire.cgi:97
-msgid "Please indicate whether you'd like to receive another questionnaire"
-msgstr "Nodwch a fyddech chi'n hoffi derbyn holiadur arall"
+#: templates/web/default/reports/council.html:17
+msgid "RSS feed of empty properties in this %s"
+msgstr "Porthiant RSS o eiddo gwag yn yr %s hwn"
-#: web/questionnaire.cgi:99
-msgid "Please provide some explanation as to why you're reopening this report"
-msgstr "Rhowch ychydig o esboniad pam rydych yn ailagor yr adroddiad hwn"
+#: perllib/FixMyStreet/Cobrand/Default.pm:695
+#: perllib/FixMyStreet/Cobrand/EmptyHomes.pm:162
+#: perllib/FixMyStreet/Cobrand/FiksGataMi.pm:122
+#, fuzzy
+msgid "RSS feed of empty properties within %s"
+msgstr "Porthiant RSS o eiddo gwag yn yr %s hwn"
-#: web/questionnaire.cgi:115
-msgid "Please provide some text as well as a photo"
-msgstr "Rhowch rywfaint o destun yn ogystal â ffotograff"
+#: perllib/FixMyStreet/Cobrand/Default.pm:702
+#: perllib/FixMyStreet/Cobrand/EmptyHomes.pm:168
+#, fuzzy
+msgid "RSS feed of empty properties within %s ward"
+msgstr "Porthiant RSS o eiddo gwag yn yr %s hwn"
-#: web/questionnaire.cgi:146
-msgid "Questionnaire filled in by empty property reporter"
-msgstr "Llenwyd yr holiadur gan yr unigolyn a roddodd wybod am yr eiddo gwag"
+#: templates/web/default/around/display_location.html:1
+#: templates/web/default/around/display_location.html:4
+msgid "RSS feed of recent local empty properties"
+msgstr "Porthiant RSS o eiddo gwag lleol diweddar"
-#: web/questionnaire.cgi:166
-msgid ""
-"<p>Thank you very much for filling in our questionnaire; if you\n"
-"get some more information about the status of your empty property, please "
-"come back to the\n"
-"site and leave an update.</p>\n"
-msgstr ""
-"<p>Diolch yn fawr am lenwi ein holiadur; os ydych yn cael \n"
-"mwy o wybodaeth am statws eich eiddo gwag, dewch yn ôl i'r safle\n"
-"a rhowch ddiweddariad yno.</p>\n"
+#: templates/web/default/report/display.html:55
+msgid "RSS feed of updates to this empty property"
+msgstr "Porthiant RSS o ddiweddariadau i'r eiddo gwag hwn"
-#: web/questionnaire.cgi:174
-#, fuzzy, perl-format
-msgid ""
-"<p style=\"font-size:150%%\">We're sorry to hear that. We have two "
-"suggestions: why not try\n"
-"<a href=\"%s\">writing direct to your councillor(s)</a>\n"
-"or, if it's an empty property that could be returned to use by local people "
-"working together,\n"
-"why not <a href=\"http://www.pledgebank.com/new\">make and publicise a "
-"pledge</a>?\n"
-"</p>\n"
+#: templates/web/default/alert/updates.html:9
+#: templates/web/default/report/display.html:45
+msgid "Receive email when updates are left on this empty property."
msgstr ""
-"<p style=\"font-size:150%\">Mae'n flin gennym glywed hynny. Mae gennym ddau "
-"awgrym: beth am roi cynnig ar\n"
-"<a href=\"http://www.writetothem.com/\">ysgrifennu'n uniongyrchol at eich "
-"cynghorwr(wyr)</a>\n"
-"neu, os yw'n eiddo gwag y gallai pobl leol gydweithio er mwyn ei adfer i'w "
-"ddefnyddio,\n"
-"beth am <a href=\"http://www.pledgebank.com/new\">wneud adduned a'i "
-"chyhoeddi</a>?\n"
-"</p>\n"
+"Derbyn neges e-bost pan fydd diweddariadau'n cael eu rhoi am yr eiddo gwag "
+"hwn."
-#: web/questionnaire.cgi:183
-msgid ""
-"<p style=\"font-size:150%\">Thank you very much for filling in our "
-"questionnaire; glad to hear it's been returned to use.</p>\n"
-msgstr ""
-"<p style=\"font-size:150%\">Diolch yn fawr am lenwi'n holiadur; rydym yn "
-"falch o glywed ei fod wedi'i adfer i'w ddefnyddio.</p>\n"
+#: templates/web/default/around/display_location.html:0
+#: templates/web/default/around/display_location.html:35
+msgid "Recent local empty properties, reportemptyhomes.com"
+msgstr "Eiddo gwag lleol diweddar, reportemptyhomes.com"
-#: web/questionnaire.cgi:236
-msgid "Don&rsquo;t know"
-msgstr "Ddim yn gwybod"
+#: templates/web/default/reports/council.html:68
+#: templates/web/default/reports/council.html:69
+#: templates/web/default/reports/index.html:14
+#: templates/web/emptyhomes/reports/index.html:13
+#: templates/web/fiksgatami/reports/index.html:13
+msgid "Recently returned to use"
+msgstr "Wedi'u hadfer i'w defnyddio'n ddiweddar"
-#: web/questionnaire.cgi:237
-msgid "Submit questionnaire"
-msgstr "Anfon yr holiadur"
+#: templates/web/default/index.html:87
+msgid "Recently reported empty properties"
+msgstr "Eiddo gwag yr adroddwyd amdanynt yn ddiweddar"
-#: web/questionnaire.cgi:254
+#: templates/web/default/report/new/notes.html:9
msgid ""
-"<p>Getting empty homes back into use can be difficult. You shouldn't expect\n"
-"the property to be back into use yet. But a good council will have started "
-"work\n"
-"and should have reported what they have done on the website. If you are not\n"
-"satisfied with progress or information from the council, now is the right "
-"time\n"
-"to say. You may also want to try contacting some other people who may be "
-"able\n"
-"to help. For advice on how to do this and other useful information please\n"
-"go to <a href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://"
-"www.emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
+"Remember that reportemptyhomes.com is primarily for reporting physical empty "
+"properties that can be returned to use. If your empty property is not "
+"appropriate for submission via this site remember that you can contact your "
+"council directly using their own website."
msgstr ""
-"<p>Mae cael eiddo gwag yn ôl mewn defnydd yn gallu bod yn anodd. Ni ddylech "
-"ddisgwyl\n"
-"bod yr eiddo yn ôl mewn defnydd eto. Fodd bynnag, bydd cyngor da eisoes wedi "
-"dechrau gweithio\n"
-"a dylent fod wedi rhoi gwybod beth maent wedi'i wneud ar y wefan. Os nad "
-"ydych yn fodlon\n"
-"â'r cynnydd neu'r wybodaeth gan y cyngor, nawr yw'r amser priodol i ddweud "
-"hynny.\n"
-"Efallai hefyd y byddwch eisiau rhoi cynnig ar gysylltu â rhai pobl eraill a "
-"all helpu.\n"
-"I gael cyngor ar sut i wneud hyn ac am wybodaeth ddefnyddiol arall, ewch i \n"
-"<a href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://www."
-"emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
-#: web/questionnaire.cgi:264
-msgid ""
-"<p>Getting empty homes back into use can be difficult, but by now a good "
-"council\n"
-"will have made a lot of progress and reported what they have done on the\n"
-"website. Even so properties can remain empty for many months if the owner "
-"is\n"
-"unwilling or the property is in very poor repair. If nothing has happened "
-"or\n"
-"you are not satisfied with the progress the council is making, now is the "
-"right\n"
-"time to say so. We think it's a good idea to contact some other people who\n"
-"may be able to help or put pressure on the council For advice on how to do\n"
-"this and other useful information please go to <a\n"
-"href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://www."
-"emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
+#: templates/web/default/admin/report_edit.html:43
+#: templates/web/default/admin/update_edit.html:33
+msgid "Remove photo (can't be undone!)"
msgstr ""
-"<p>Mae cael eiddo gwag yn ôl mewn defnydd yn gallu bod yn anodd, ond erbyn "
-"hyn,\n"
-"bydd cyngor da wedi gwneud eithaf tipyn o gynnydd ac wedi rhoi gwybod beth "
-"maent\n"
-"wedi'i wneud ar y wefan. Er hynny, gall eiddo gwag barhau'n wag am fisoedd "
-"os yw'r perchennog\n"
-"yn amharod neu os yw cyflwr yr eiddo'n wael. Os nad oes unrhyw beth wedi "
-"digwydd neu\n"
-"os nad ydych yn fodlon â'r cynnydd y mae'r cyngor yn ei wneud, nawr yw'r "
-"amser priodol\n"
-"i ddweud hynny. Rydym o'r farn bod cysylltu â phobl eraill a allai helpu neu "
-"roi pwysau\n"
-"ar y cyngor yn syniad da. I gael cyngor ar sut i wneud hyn \n"
-"a gwybodaeth ddefnyddiol arall, ewch i <a\n"
-"href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://www."
-"emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
-#: web/questionnaire.cgi:277
-msgid ""
-"The details of your empty property are available on the right hand side of "
-"this page."
-msgstr "Mae manylion eich eiddo gwag ar gael ar ochr dde'r dudalen hon."
+#: perllib/FixMyStreet/Cobrand/EmptyHomes.pm:77
+msgid "Report Empty Homes"
+msgstr "Adrodd am Eiddo Gwag"
-#: web/questionnaire.cgi:278
-msgid "Please take a look at the updates that have been left."
-msgstr "Ewch i fwrw golwg ar y diweddariadau sydd wedi cael eu gadael."
+#: templates/web/default/footer.html:6 templates/web/emptyhomes/header.html:30
+#: templates/web/fiksgatami/footer.html:5
+msgid "Report a property"
+msgstr "Rhoi gwybod am eiddo"
-#: web/questionnaire.cgi:284
-msgid "An update marked this empty property as returned to use."
-msgstr ""
-"Fe wnaeth diweddariad gofnodi bod yr eiddo hwn wedi cael ei adfer i'w "
-"ddefnyddio."
+#: perllib/FixMyStreet/App/Controller/Rss.pm:262
+#, fuzzy
+msgid "Report on %s"
+msgstr "Adroddiadau"
-#: web/questionnaire.cgi:285
-msgid "Has this empty property been returned to use?"
-msgstr "A yw'r eiddo gwag hwn wedi cael ei adfer i'w ddefnyddio?"
+#: templates/web/default/index.html:11
+msgid "Report and view empty properties"
+msgstr "Adrodd am eiddo gwag a gweld y rhain"
+
+#: perllib/FixMyStreet/DB/Result/Problem.pm:331
+#: templates/web/default/contact/index.html:45
+msgid "Reported anonymously at %s"
+msgstr "Adroddwyd yn ddi-enw am %s"
-#: web/questionnaire.cgi:292 web-admin/index.cgi:889
+#: templates/web/default/admin/questionnaire.html:5
+#: templates/web/default/questionnaire/index.html:79
msgid "Reported before"
msgstr "Wedi adrodd o'r blaen"
-#: web/questionnaire.cgi:293
-msgid "First time"
-msgstr "Y tro cyntaf"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:323
+msgid "Reported by %s anonymously at %s"
+msgstr "Adroddwyd gan %s yn ddi-enw am %s"
-#: web/questionnaire.cgi:294
-msgid ""
-"Have you ever reported an empty property to a council before, or is this "
-"your first time?"
-msgstr ""
-"Ydych chi wedi rhoi gwybod i gyngor am eiddo gwag erioed o'r blaen, neu ai "
-"dyma'ch tro cyntaf?"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:354
+#: templates/web/default/contact/index.html:47
+msgid "Reported by %s at %s"
+msgstr "Adroddwyd gan %s am %s"
-#: web/questionnaire.cgi:304
-msgid ""
-"If you wish to leave a public update on the empty property, please enter it "
-"here\n"
-"(please note it will not be sent to the council). For example, what was\n"
-"your experience of getting the empty property returned to use?"
-msgstr ""
-"Os ydych yn dymuno gadael diweddariad cyhoeddus am yr eiddo gwag, cofnodwch "
-"hwn yma\n"
-"(sylwer na fydd yn cael ei anfon at y cyngor). Er enghraifft, beth oedd eich "
-"profiad\n"
-"o gael yr eiddo gwag yn ôl mewn defnydd?"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:345
+msgid "Reported by %s by %s at %s"
+msgstr "Adroddwyd gan %s trwy %s am %s"
-#: web/questionnaire.cgi:322
-msgid ""
-"Would you like to receive another questionnaire in 4 weeks, reminding you to "
-"check the status?"
-msgstr ""
+#: perllib/FixMyStreet/DB/Result/Problem.pm:319
+msgid "Reported by %s in the %s category anonymously at %s"
+msgstr "Adroddwyd am hyn gan %s yn y categori %s yn ddi-enw, am %s"
-#: web/reports.cgi:192 web/reports.cgi:239
-msgid "Summary reports"
-msgstr "Adroddiadau cryno"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:339
+msgid "Reported by %s in the %s category by %s at %s"
+msgstr "Adroddwyd gan %s yn y categori %s trwy %s am %s"
-#: web/reports.cgi:194
-msgid ""
-"This is a summary of all reports on this site; select a particular council "
-"to see the reports sent there."
-msgstr ""
-"Dyma grynodeb o'r holl adroddiadau ar y safle hwn; dewiswch gyngor penodol i "
-"weld yr adroddiadau a anfonwyd ato."
+#: perllib/FixMyStreet/DB/Result/Problem.pm:327
+msgid "Reported in the %s category anonymously at %s"
+msgstr "Adroddiwyd yn y categori %s yn ddi-enw am %s"
-#: web/reports.cgi:195
-msgid "Greyed-out lines are councils that no longer exist."
-msgstr "Mae'r llinellau mewn llwyd yn gynghorau nad ydynt yn bodoli mwyach."
+#: perllib/FixMyStreet/DB/Result/Problem.pm:349
+msgid "Reported in the %s category by %s at %s"
+msgstr "Adroddwyd yn y categori %s gan %s am %s"
-#: web/reports.cgi:199 web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "Name"
-msgstr "Enw"
+#: templates/web/default/around/around_index.html:1
+#: templates/web/default/report/new/fill_in_details.html:0
+#: templates/web/default/report/new/fill_in_details.html:3
+#: templates/web/default/report/new/fill_in_details.html:30
+msgid "Reporting an empty property"
+msgstr "Adrodd am eiddo gwag"
-#: web/reports.cgi:199 web/reports.cgi:275
-msgid "New empty property reports"
-msgstr "Adroddiadau newydd am eiddo gwag"
+#: templates/web/default/around/display_location.html:103
+msgid "Reports on and around the map"
+msgstr "Adroddiadau ar ac yng nghyffiniau'r map"
-#: web/reports.cgi:199 web/reports.cgi:283
-msgid "Older empty property reports"
-msgstr "Adroddiadau hÅ·n o eiddo gwag"
+#: templates/web/default/admin/report_edit.html:32
+#, fuzzy
+msgid "Resend report"
+msgstr "Cuddio hen adroddiadau "
-#: web/reports.cgi:201
-msgid "Old empty properties,<br>state unknown"
-msgstr "Hen eiddo gwag,<br>cyflwr anhysbys"
+#: templates/web/default/admin/council_edit.html:35
+msgid "Save changes"
+msgstr ""
-#: web/reports.cgi:203 web/reports.cgi:290
-msgid "Recently returned to use"
-msgstr "Wedi'u hadfer i'w defnyddio'n ddiweddar"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:599
+#: templates/web/default/admin/search_reports.html:1
+#, fuzzy
+msgid "Search Reports"
+msgstr "Adroddiadau cryno"
-#: web/reports.cgi:203
-msgid "Older returned to use"
-msgstr "Wedi'u hadfer i'w defnyddio ers amser"
+#: templates/web/default/admin/search_reports.html:5
+msgid "Search:"
+msgstr ""
-#: web/reports.cgi:245
-msgid "council"
-msgstr "cyngor"
+#: templates/web/default/alert/list.html:41
+#, fuzzy
+msgid ""
+"Select which type of alert you'd like and click the button for an RSS feed, "
+"or enter your email address to subscribe to an email alert."
+msgstr ""
+"Dewiswch y math o hysbysiad yr hoffech ei gael a chliciwch y botwm ar gyfer "
+"porthiant RSS,\n"
+"neu rhowch eich cyfeiriad e-bost i danysgrifio am hysbysiad e-bost."
-#: web/reports.cgi:255
-#, perl-format
-msgid "RSS feed of empty properties in this %s"
-msgstr "Porthiant RSS o eiddo gwag yn yr %s hwn"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:378
+msgid "Sent to %s %s later"
+msgstr "Anfonwyd at %s %s yn ddiweddarach"
-#: web/reports.cgi:259
-#, perl-format
-msgid "This is a summary of all reports for one %s."
-msgstr "Dyma grynodeb o'r holl adroddiadau ar gyfer y %s."
+#: templates/web/default/admin/report_edit.html:32
+#, fuzzy
+msgid "Sent:"
+msgstr "Pwnc:"
-#: web/reports.cgi:260
-#, perl-format
-msgid "This is a summary of all reports for this %s."
+#: templates/web/default/admin/report_edit.html:34
+msgid "Service:"
msgstr ""
-#: web/reports.cgi:264
-#, perl-format
-msgid "You can <a href=\"%s\">see less detail</a>."
-msgstr "Gallwch <a href=\"%s\">weld llai o fanylion</a>."
+#: templates/web/emptyhomes/static/about.html:21
+msgid "Shelter Cymru"
+msgstr ""
-#: web/reports.cgi:266
-#, perl-format
-msgid "You can <a href=\"%s\">see more details</a>."
-msgstr "Gallwch <a href=\"%s\">weld mwy o fanylion</a>."
+#: templates/web/emptyhomes/static/about.html:23
+#, fuzzy
+msgid ""
+"Shelter Cymru is Wales&rsquo; people and homes charity and wants\n"
+" everyone in Wales to have a decent home. We believe a home is a "
+"fundamental\n"
+" right and essential to the health and well-being of people and "
+"communities.\n"
+" We work for people in housing need. We have offices all over Wales and\n"
+" prevent people from losing their homes by offering free, confidential "
+"and\n"
+" independent advice. When necessary we constructively challenge on behalf "
+"of\n"
+" people to ensure they are properly assisted and to improve practice and\n"
+" learning. We believe that bringing empty homes back into use can make a\n"
+" significant contribution to the supply of affordable homes in Wales."
+msgstr ""
+"<h2>Shelter Cymru</h2>\n"
+"Shelter Cymru yw&rsquo;r elusen yng Nghymru ar gyfer pobl a chartrefi ac "
+"rydym\n"
+"eisiau i bawb yng Nghymru gael cartref addas. Credwn fod cartref yn hawl\n"
+"sylfaenol a&rsquo;i fod yn hanfodol i iechyd a lles pobl a chymunedau. Rydym "
+"yn\n"
+"gweithio dros bobl sydd mewn angen am dai. Mae gennym swyddfeydd ar draws "
+"Cymru\n"
+"ac rydym yn atal pobl rhag colli eu cartrefi drwy gynnig cyngor annibynnol,\n"
+"cyfrinachol, am ddim. Pan fo&rsquo;r angen, rydym yn herio&rsquo;n adeiladol "
+"ar ran pobl er\n"
+"mwyn sicrhau eu bod yn cael eu cynorthwyo&rsquo;n gywir ac i wella ymarfer a "
+"dysgu.\n"
+"Mae Shelter Cymru yn credu y gall gwneud gwell defnydd o gartrefi gwag\n"
+"gyfrannu&rsquo;n sylweddol at y ddaprariaeth o dai fforddiadwy yng Nghymru.\n"
+"<a href='http://www.sheltercymru.org.uk/shelter/cymraeg/advice/pdetail.asp?"
+"cat=20'>Gwybodaeth\n"
+"bellach am ein gwaith ar gartrefi gwag</a>.\n"
-#: web/reports.cgi:268
-#, perl-format
+#: templates/web/default/around/display_location.html:52
+msgid "Show pins"
+msgstr "Dangos pinnau"
+
+#: templates/web/default/alert/index.html:31
+msgid "Some photos of recent reports"
+msgstr "Rhai ffotograffau o adroddiadau diweddar"
+
+#: perllib/FixMyStreet/App/View/Email.pm:32
+#: perllib/FixMyStreet/App/View/Web.pm:38
+msgid "Some text to localize"
+msgstr ""
+
+#: templates/web/default/admin/council_list.html:42
+msgid "Some unconfirmeds"
+msgstr ""
+
+#: perllib/FixMyStreet/Geocode.pm:171
msgid ""
-"You can <a href=\"%s\">see less detail</a> or go back and <a href=\"/reports"
-"\">show all councils</a>."
+"Sorry, that appears to be a Crown dependency postcode, which we don't cover."
msgstr ""
-"Gallwch <a href=\"%s\">weld llai o fanylion</a> neu fynd yn ôl a <a href=\"/"
-"reports\">dangos pob cyngor</a>."
-#: web/reports.cgi:270
-#, perl-format
+#: perllib/FixMyStreet/Geocode.pm:71
msgid ""
-"You can <a href=\"%s\">see more details</a> or go back and <a href=\"/reports"
-"\">show all councils</a>."
+"Sorry, that location appears to be too general; please be more specific."
msgstr ""
-"Gallwch <a href=\"%s\">weld mwy o fanylion</a> neu fynd yn ôl a <a href=\"/"
-"reports\">dangos pob cyngor</a>."
+"Sori, mae'n ymddangos bod y lleoliad hwnnw'n rhy gyffredinol; rhowch gynnig "
+"arall arni."
-#: web/reports.cgi:285
-msgid "Old empty properties, state unknown"
-msgstr "Eiddo gwag hen, nid yw eu cyflwr yn hysbys"
+#: templates/web/default/tokens/abuse.html:5
+msgid "Sorry, there has been an error confirming your empty property."
+msgstr "Sori, bu gwall wrth gadarnhau eich eiddo gwag."
-#: web/reports.cgi:291
-msgid "Old returned to use"
-msgstr "Hen wedi'i adfer i'w ddefnyddio"
+#: perllib/FixMyStreet/Geocode.pm:103 perllib/FixMyStreet/Geocode.pm:150
+msgid "Sorry, we could not find that location."
+msgstr "Sori, ni fu modd i ni ddod o hyd i'r lleoliad hwnnw."
-#: web/reports.cgi:294
-#, perl-format
-msgid "%s - Summary reports"
-msgstr "%s - Adroddiadau cryno"
+#: perllib/FixMyStreet/Geocode.pm:148
+msgid "Sorry, we could not parse that location. Please try again."
+msgstr ""
+"Sori, ni fu modd i ni ddosrannu'r lleoliad hwnnw. Rhowch gynnig arall arni."
-#: web/reports.cgi:294
-#, perl-format
-msgid "Empty properties within %s, reportemptyhomes.com"
-msgstr "Eiddo gwag o fewn %s, reportemptyhomes.com"
+#: templates/web/default/admin/list_updates.html:6
+#: templates/web/default/admin/search_reports.html:21
+#, fuzzy
+msgid "State"
+msgstr "Diweddariad:"
-#: web/reports.cgi:333
+#: templates/web/default/admin/report_edit.html:21
+#: templates/web/default/admin/update_edit.html:18
#, fuzzy
-msgid "(sent to both)"
-msgstr "Ni roddwyd gwybod i'r cyngor am hyn"
+msgid "State:"
+msgstr "Diweddariad:"
-#: web/reports.cgi:334
+#: templates/web/default/admin/report_edit.html:13
+#: templates/web/default/contact/index.html:83
+#: templates/web/default/report/new/fill_in_details.html:91
+msgid "Subject:"
+msgstr "Pwnc:"
+
+#: templates/web/default/questionnaire/creator_fixed.html:19
+#: templates/web/default/report/new/fill_in_details.html:171
+msgid "Submit"
+msgstr "Anfon"
+
+#: templates/web/default/admin/report_edit.html:46
+#: templates/web/default/admin/update_edit.html:36
#, fuzzy
-msgid "(not sent to council)"
-msgstr "Ni roddwyd gwybod i'r cyngor am hyn"
+msgid "Submit changes"
+msgstr "Anfon"
-#: bin/send-reports:96
-msgid ""
-"This web page also contains a photo of the empty property, provided by the "
-"user."
-msgstr ""
-"Mae'r dudalen we hon hefyd yn cynnwys ffotograff o'r eiddo gwag, wedi'i "
-"ddarparu gan y defnyddiwr."
+#: templates/web/default/questionnaire/index.html:114
+msgid "Submit questionnaire"
+msgstr "Anfon yr holiadur"
-#: bin/send-reports:102
-msgid "To view a map of the precise location of this issue"
-msgstr "Gweld map o union leoliad y broblem hon"
+#: templates/web/default/alert/updates.html:17
+#: templates/web/default/report/display.html:50
+msgid "Subscribe"
+msgstr "Tanysgrifio"
-#: bin/send-reports:103
-msgid ""
-"The user could not locate the empty property on a map, but to see the area "
-"around the location they entered"
-msgstr ""
-"Ni allai'r defnyddiwr leoli'r eiddo gwag ar fap, ond i weld yr ardal o "
-"gwmpas y lleoliad a gofnodwyd ganddynt "
+#: templates/web/default/alert/list.html:127
+msgid "Subscribe me to an email alert"
+msgstr "Dymunaf danysgrifio i rybuddion drwy e-bost"
-#: bin/send-reports:187
+#: perllib/FixMyStreet/App/Controller/Admin.pm:597
+#: templates/web/default/admin/index.html:1
#, fuzzy
-msgid "this type of local empty property"
-msgstr "Anfonwch fanylion eiddo gwag lleol newydd ataf i drwy'r e-bost"
+msgid "Summary"
+msgstr "Adroddiadau cryno"
-#: bin/send-reports:191
-#, fuzzy, perl-format
-msgid "Category: %s"
-msgstr "Categori:"
+#: templates/web/default/reports/index.html:1
+#: templates/web/emptyhomes/reports/index.html:1
+#: templates/web/fiksgatami/reports/index.html:1
+msgid "Summary reports"
+msgstr "Adroddiadau cryno"
-#: bin/send-reports:194
-msgid " and "
+#: perllib/FixMyStreet/App/Controller/Admin.pm:601
+#: templates/web/default/admin/questionnaire.html:1
+msgid "Survey Results"
msgstr ""
-#: bin/send-reports:196
-msgid ""
-"This email has been sent to both councils covering the location of the empty "
-"property, as the user did not categorise it; please ignore it if you're not "
-"the correct council to deal with the issue, or let us know what category of "
-"empty property this is so we can add it to our system."
+#: templates/web/default/admin/list_updates.html:12
+msgid "Text"
+msgstr ""
+
+#: templates/web/default/admin/council_contacts.html:12
+msgid "Text only version"
+msgstr ""
+
+#: templates/web/default/admin/update_edit.html:13
+msgid "Text:"
msgstr ""
-#: bin/send-reports:199
+#: templates/web/default/tokens/confirm_update.html:7
+#: templates/web/default/tokens/confirm_update.html:8
msgid ""
-"This email has been sent to several councils covering the location of the "
-"empty property, as the category selected is provided for all of them; please "
-"ignore it if you're not the correct council to deal with the issue."
+"Thank you &mdash; you can <a href=\"%s\">view your updated empty property</"
+"a> on the site."
msgstr ""
+"Diolch &mdash; gallwch <a href=\"%s\">weld diweddariad eich eiddo gwag</a> "
+"ar y safle."
-#: bin/send-reports:206
-#, perl-format
+#: templates/web/emptyhomes/tokens/confirm_problem.html:6
+#: templates/web/emptyhomes/tokens/confirm_problem.html:8
+#, fuzzy
msgid ""
-"We realise this empty property might be the responsibility of %s; however, "
-"we don't currently have any contact details for them. If you know of an "
-"appropriate contact address, please do get in touch."
+"Thank you for reporting an empty property on ReportEmptyHomes.com. We have "
+"emailed the lead officer for empty homes in the council responsible with "
+"details, and asked them to do whatever they can to get the empty property "
+"back into use as soon as possible."
msgstr ""
+"Diolch am roi gwybod am eiddo gwag ar\n"
+"ReportEmptyHomes.com. Rydym wedi anfon neges e-bost gyda'r manylion at y "
+"swyddog eiddo gwag yn \n"
+"y cyngor cyfrifol ac wedi gofyn iddynt wneud beth bynnag y gallant i helpu "
+"adfer yr eiddo gwag\n"
+"fel ei fod yn cael ei ddefnyddio eto cyn gynted ag y bo modd."
-#: db/alert_types_eha.pl:5
-msgid "New reports on reportemptyhomes.com"
-msgstr "Adroddiadau newydd ar reportemptyhomes.com"
+#: templates/web/emptyhomes/tokens/confirm_problem.html:30
+#: templates/web/emptyhomes/tokens/confirm_problem.html:31
+msgid ""
+"Thank you for reporting this empty property on ReportEmptyHomes.com.\n"
+"At present the report cannot be sent through to the council for this area. "
+"We\n"
+"are working with councils to link them into the system so that as many "
+"areas\n"
+"as possible will be covered."
+msgstr "Diolch am roi gwybod am eiddo gwag ar ReportEmptyHomes.com."
-#: db/alert_types_eha.pl:8
-msgid "Properties recently reported as put back to use on reportemptyhomes.com"
+#: templates/web/default/tokens/error.html:7
+#, fuzzy
+msgid ""
+"Thank you for trying to confirm your update or empty property. We seem to "
+"have an error ourselves though, so <a href=\"%s\">please let us know what "
+"went on</a> and we'll look into it."
msgstr ""
-"Eiddo y cafwyd adroddiadau diweddar ar reportemptyhomes.com eu bod wedi cael "
-"eu hadfer i'w defnyddio"
+"Diolch am geisio cadarnhau eich diweddariad neu eich eiddo gwag. Fodd "
+"bynnag, \n"
+"mae'n ymddangos bod gwall gennym ein hunain, felly <a href=\"%s\">rhowch "
+"wybod i ni beth ddigwyddodd</a>\n"
+"ac fe wnawn ni edrych yn fanylach i hyn.\n"
-#: db/alert_types_eha.pl:9
-msgid "The latest properties reported back to use by users"
+#: templates/web/emptyhomes/tokens/confirm_problem.html:24
+#: templates/web/emptyhomes/tokens/confirm_problem.html:26
+#, fuzzy
+msgid ""
+"Thank you for using ReportEmptyHomes.com. Your action is already helping to "
+"resolve the UK&rsquo;s empty homes crisis."
msgstr ""
-"Yr eiddo diweddaraf y mae defnyddwyr wedi rhoi gwybod eu bod yn ôl mewn "
-"defnyd"
-
-#: db/alert_types_eha.pl:12
-msgid "New local reports on reportemptyhomes.com"
-msgstr "Adroddiadau lleol newydd ar reportemptyhomes.com"
+"Diolch am ddefnyddio ReportEmptyHomes.com. Mae eich gweithred eisoes yn "
+"helpu i \n"
+"ddatrys argyfwng tai gwag y DU."
-#: db/alert_types_eha.pl:13
-msgid "The latest local reports reported by users"
+#: templates/web/default/around/around_index.html:44
+msgid ""
+"Thanks for uploading your photo. We now need to locate your empty property, "
+"so please enter a nearby street name or postcode in the box below&nbsp;:"
msgstr ""
-"Yr adroddiadau lleol diweddaraf y mae defnyddwyr wedi rhoi gwybod amdanynt"
-
-#: db/alert_types_eha.pl:16
-#, fuzzy, perl-brace-format
-msgid "New reports on reportemptyhomes.com near {{POSTCODE}}"
-msgstr "Adroddiadau newydd ar reportemptyhomes.com"
+"Diolch am lwytho'ch ffotograff i fyny. Nawr mae angen i ni ddod o hyd i "
+"leoliad eich eiddo gwag, felly rhowch enw stryd gerllaw neu god post yn y "
+"blwch isod&nbsp;:"
-#: db/alert_types_eha.pl:19
-#, perl-brace-format
-msgid "New reports to {{COUNCIL}} on reportemptyhomes.com"
-msgstr "Adroddiadau newydd i gyngor {{COUNCIL}} ar reportemptyhomes.com"
+#: templates/web/default/contact/submit.html:8
+msgid "Thanks for your feedback. We'll get back to you as soon as we can!"
+msgstr "Diolch am eich adborth. Byddwn yn eich ateb cyn gynted ag y gallwn!"
-#: db/alert_types_eha.pl:20
-#, perl-brace-format
-msgid "The latest reports for {{COUNCIL}} reported by users"
+#: templates/web/default/questionnaire/creator_fixed.html:9
+msgid ""
+"Thanks, glad to hear it's been returned to use! Could we just ask if you "
+"have ever reported an empty property to a council before?"
msgstr ""
-"Yr adroddiadau diweddaraf ar gyfer cyngor {{COUNCIL}} y mae defnyddwyr wedi "
-"rhoi gwybod amdanynt"
+"Diolch, falch o glywed ei fod wedi cael ei adfer i'w ddefnyddio! Fyddai modd "
+"i ni'ch holi chi, a ydych erioed wedi rhoi gwybod i'r cyngor am eiddo gwag "
+"cyn hyn?"
-#: db/alert_types_eha.pl:23
-#, perl-brace-format
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:738
msgid ""
-"New reports for {{COUNCIL}} within {{WARD}} ward on reportemptyhomes.com"
+"That image doesn't appear to have uploaded correctly (%s), please try again."
msgstr ""
-"Adroddiadau newydd ar gyfer cyngor {{COUNCIL}}, yn ward {{WARD}} ar "
-"reportemptyhomes.com"
+"Nid yw'n ymddangos bod y ddelwedd honno wedi llwytho i fyny'n gywir (%s), "
+"rhowch gynnig arni eto."
-#: db/alert_types_eha.pl:24
-#, perl-brace-format
+#: templates/web/default/alert/index.html:12
msgid ""
-"The latest reports for {{COUNCIL}} within {{WARD}} ward reported by users"
+"That location does not appear to be covered by a council, perhaps it is "
+"offshore - please try somewhere more specific."
msgstr ""
-"Yr adroddiadau diweddaraf ar gyfer cyngor {{COUNCIL}}, yn ward {{WARD}} y "
-"mae defnyddwyr wedi rhoi gwybod amdanynt"
-
-#: db/alert_types_eha.pl:27
-#, perl-brace-format
-msgid "New reports within {{NAME}}'s boundary on reportemptyhomes.com"
-msgstr "Adroddiadau newydd o fewn ffin {{NAME}} ar reportemptyhomes.com"
+"Nid yw'n ymddangos bod cyngor yn gyfrifol am y lleoliad hwnnw, efallai ei "
+"fod ar y môr - chwiliwch am fan mwy penodol."
-#: db/alert_types_eha.pl:28
-#, perl-brace-format
-msgid "The latest reports within {{NAME}}'s boundary reported by users"
+#: perllib/FixMyStreet/Geocode.pm:81
+msgid "That location does not appear to be in Britain; please try again."
msgstr ""
-"Yr adroddiadau diweddaraf o fewn ffin {{NAME}} y mae defnyddwyr wedi rhoi "
-"gwybod amdanynt"
-
-#: db/alert_types.pl:5 db/alert_types.pl:6
-#, perl-brace-format
-msgid "Updates on {{title}}"
-msgstr "Diweddariadau am {{title}}"
+"Nid yw'n ymddangos bod y lleoliad hwnnw ym Mhrydain; rhowch gynnig arall "
+"arni."
-#: db/alert_types.pl:7
-#, perl-brace-format
-msgid "Update by {{name}}"
-msgstr "Diweddariadau yn ôl {{name}}"
+#: perllib/FixMyStreet/Geocode.pm:165
+msgid "That postcode was not recognised, sorry."
+msgstr "Ni chafodd y cod post hwnnw ei gydnabod, sori."
-#: db/alert_types.pl:10
+#: perllib/FixMyStreet/App/Controller/Admin.pm:498
#, fuzzy
-msgid "New empty property reports on reportemptyhomes.com"
-msgstr "Adroddiadau newydd ar reportemptyhomes.com"
+msgid "That empty property will now be resent."
+msgstr "ni fydd eich eiddo gwag yn cael ei bostio"
-#: db/alert_types.pl:11
-msgid "The latest empty properties reported by users"
-msgstr "Yr eiddo gwag diweddaraf y mae defnyddwyr wedi rhoi gwybod amdanynt"
+#: perllib/FixMyStreet/App/Controller/Report.pm:75
+#: perllib/FixMyStreet/App/Controller/Rss.pm:41
+msgid "That report has been removed from reportemptyhomes.com."
+msgstr "Mae'r adroddiad hwnnw wedi cael ei dynnu oddi ar reportemptyhomes.com"
-#: db/alert_types.pl:14
+#: templates/web/default/around/around_index.html:27
#, fuzzy
msgid ""
-"Empty properties recently reported returned to use on reportemptyhomes.com"
+"That spot does not appear to be covered by a council. If you have tried to "
+"report an issue past the shoreline, for example, please specify the closest "
+"point on land."
msgstr ""
-"Eiddo y cafwyd adroddiadau diweddar ar reportemptyhomes.com eu bod wedi cael "
-"eu hadfer i'w defnyddio"
+"Nid yw'n ymddangos bod cyngor yn gyfrifol am y lleoliad hwnnw.\n"
+"Os ydych wedi ceisio adrodd am broblem oddi ar y lân, er enghraifft,\n"
+"dylech nodi'r pwynt agosaf ar y tir."
-#: db/alert_types.pl:15
+#: templates/web/emptyhomes/static/about.html:7
#, fuzzy
-msgid "The latest empty properties reported returned to use by users"
-msgstr "Yr eiddo gwag diweddaraf y mae defnyddwyr wedi rhoi gwybod amdanynt"
+msgid "The Empty Homes Agency"
+msgstr "Yr Asiantaeth Tai Gwag"
-#: db/alert_types.pl:18 db/alert_types.pl:22
+#: templates/web/emptyhomes/static/about.html:9
#, fuzzy
-msgid "New local empty properties on reportemptyhomes.com"
-msgstr "Eiddo gwag lleol diweddar, reportemptyhomes.com"
+msgid ""
+"The Empty Homes agency is an independent campaigning charity. We\n"
+" are not part of government, and have no formal links with local "
+"councils\n"
+" although we work in cooperation with both. We exist to highlight the "
+"waste\n"
+" of empty property and work with others to devise and promote "
+"sustainable\n"
+" solutions to bring empty property back into use. We are based in London "
+"but\n"
+" work across England. We also work in partnership with other charities "
+"across\n"
+" the UK."
+msgstr ""
+"<h2>Yr Asiantaeth Tai Gwag</h2>\n"
+"<p>Mae'r Asiantaeth Tai Gwag yn elusen ymgyrchu annibynnol. Nid ydym yn rhan "
+"o'r\n"
+"llywodraeth, ac nid oes gennym unrhyw gysylltiadau ffurfiol â chynghorau "
+"lleol er ein bod\n"
+"yn cydweithio â'r naill a'r llall. Rydym yn bodoli er mwyn amlygu gwastraff "
+"yr eiddo gwag ac\n"
+"yn gweithio gyda eraill er mwyn dyfeisio a hyrwyddo atebion cynaliadwy er "
+"mwyn adfer \n"
+"eiddo gwag yn eiddo y mae pobl yn byw ynddynt eto. Rydym wedi'n lleoli yn "
+"Llundain ond\n"
+"rydym yn gweithio ledled Lloegr. Rydym hefyd yn gweithio mewn partneriaeth "
+"ag elusennau eraill ledled y DU.</p>\n"
+
+#: templates/web/default/email_sent.html:26
+msgid ""
+"The confirmation email <strong>may</strong> take a few minutes to arrive "
+"&mdash; <em>please</em> be patient."
+msgstr ""
+
+#: templates/web/default/questionnaire/index.html:51
+msgid ""
+"The details of your empty property are available on the right hand side of "
+"this page."
+msgstr "Mae manylion eich eiddo gwag ar gael ar ochr dde'r dudalen hon."
#: db/alert_types.pl:19 db/alert_types.pl:23 db/alert_types.pl:27
#: db/alert_types.pl:31
@@ -2117,39 +2223,20 @@ msgstr "Eiddo gwag lleol diweddar, reportemptyhomes.com"
msgid "The latest local empty properties reported by users"
msgstr "Yr eiddo gwag diweddaraf y mae defnyddwyr wedi rhoi gwybod amdanynt"
-#: db/alert_types.pl:26
-#, fuzzy, perl-brace-format
-msgid "New empty property reports near {{POSTCODE}} on reportemptyhomes.com"
-msgstr "Adroddiadau newydd i gyngor {{COUNCIL}} ar reportemptyhomes.com"
-
-#: db/alert_types.pl:30
-#, fuzzy, perl-brace-format
-msgid "New empty property reports NEAR {{POSTCODE}} on reportemptyhomes.com"
-msgstr "Adroddiadau newydd i gyngor {{COUNCIL}} ar reportemptyhomes.com"
-
-#: db/alert_types.pl:34
-#, fuzzy, perl-brace-format
-msgid "New empty property reports to {{COUNCIL}} on reportemptyhomes.com"
-msgstr "Adroddiadau newydd i gyngor {{COUNCIL}} ar reportemptyhomes.com"
+#: db/alert_types_eha.pl:13
+msgid "The latest local reports reported by users"
+msgstr ""
+"Yr adroddiadau lleol diweddaraf y mae defnyddwyr wedi rhoi gwybod amdanynt"
#: db/alert_types.pl:35
-#, fuzzy, perl-brace-format
+#, fuzzy
msgid "The latest empty properties for {{COUNCIL}} reported by users"
msgstr ""
"Yr adroddiadau diweddaraf ar gyfer cyngor {{COUNCIL}} y mae defnyddwyr wedi "
"rhoi gwybod amdanynt"
-#: db/alert_types.pl:38
-#, fuzzy, perl-brace-format
-msgid ""
-"New empty property reports for {{COUNCIL}} within {{WARD}} ward on "
-"reportemptyhomes.com"
-msgstr ""
-"Adroddiadau newydd ar gyfer cyngor {{COUNCIL}}, yn ward {{WARD}} ar "
-"reportemptyhomes.com"
-
#: db/alert_types.pl:39
-#, fuzzy, perl-brace-format
+#, fuzzy
msgid ""
"The latest empty properties for {{COUNCIL}} within {{WARD}} ward reported by "
"users"
@@ -2157,1419 +2244,770 @@ msgstr ""
"Yr adroddiadau diweddaraf ar gyfer cyngor {{COUNCIL}}, yn ward {{WARD}} y "
"mae defnyddwyr wedi rhoi gwybod amdanynt"
-#: db/alert_types.pl:42
-#, fuzzy, perl-brace-format
-msgid "New empty property reports within {{NAME}}"
-msgstr "Adroddiadau newydd am eiddo gwag"
-
-#: db/alert_types.pl:43
-#, fuzzy, perl-brace-format
-msgid "The latest empty properties within {{NAME}}"
+#: db/alert_types.pl:11
+msgid "The latest empty properties reported by users"
msgstr "Yr eiddo gwag diweddaraf y mae defnyddwyr wedi rhoi gwybod amdanynt"
-#: web-admin/index.cgi:68 web-admin/index.cgi:124 web-admin/index.cgi:125
-#, fuzzy
-msgid "Summary"
-msgstr "Adroddiadau cryno"
-
-#: web-admin/index.cgi:69 web-admin/index.cgi:183 web-admin/index.cgi:184
+#: db/alert_types.pl:15
#, fuzzy
-msgid "Council contacts"
-msgstr "Cysylltwch â Ni"
+msgid "The latest empty properties reported returned to use by users"
+msgstr "Yr eiddo gwag diweddaraf y mae defnyddwyr wedi rhoi gwybod amdanynt"
-#: web-admin/index.cgi:70 web-admin/index.cgi:465
+#: db/alert_types.pl:43
#, fuzzy
-msgid "Search Reports"
-msgstr "Adroddiadau cryno"
+msgid ""
+"The latest empty properties within {{NAME}}'s boundary reported by users"
+msgstr ""
+"Yr adroddiadau diweddaraf o fewn ffin {{NAME}} y mae defnyddwyr wedi rhoi "
+"gwybod amdanynt"
-#: web-admin/index.cgi:71 web-admin/index.cgi:784 web-admin/index.cgi:785
-msgid "Timeline"
+#: db/alert_types_eha.pl:9
+msgid "The latest properties reported back to use by users"
msgstr ""
+"Yr eiddo diweddaraf y mae defnyddwyr wedi rhoi gwybod eu bod yn ôl mewn "
+"defnyd"
-#: web-admin/index.cgi:72 web-admin/index.cgi:871 web-admin/index.cgi:872
-msgid "Survey Results"
+#: db/alert_types_eha.pl:20
+msgid "The latest reports for {{COUNCIL}} reported by users"
msgstr ""
+"Yr adroddiadau diweddaraf ar gyfer cyngor {{COUNCIL}} y mae defnyddwyr wedi "
+"rhoi gwybod amdanynt"
-#: web-admin/index.cgi:85
-#, fuzzy
-msgid "reportemptyhomes.com administration"
-msgstr "Diweddariadau i reportemptyhomes.com"
+#: db/alert_types_eha.pl:24
+msgid ""
+"The latest reports for {{COUNCIL}} within {{WARD}} ward reported by users"
+msgstr ""
+"Yr adroddiadau diweddaraf ar gyfer cyngor {{COUNCIL}}, yn ward {{WARD}} y "
+"mae defnyddwyr wedi rhoi gwybod amdanynt"
-#: web-admin/index.cgi:101
-#, fuzzy
-msgid "reportemptyhomes.com admin:"
-msgstr "reportemptyhomes.com"
+#: db/alert_types_eha.pl:28
+msgid "The latest reports within {{NAME}}'s boundary reported by users"
+msgstr ""
+"Yr adroddiadau diweddaraf o fewn ffin {{NAME}} y mae defnyddwyr wedi rhoi "
+"gwybod amdanynt"
-#: web-admin/index.cgi:154
-#, fuzzy, perl-format
-msgid "<strong>%d</strong> live empty properties"
-msgstr "Adrodd am eiddo gwag a gweld y rhain"
+#: templates/web/default/auth/change_password.html:12
+#: templates/web/default/auth/change_password.html:16
+msgid "The passwords do not match"
+msgstr ""
-#: web-admin/index.cgi:155
-#, fuzzy, perl-format
-msgid "%d live updates"
-msgstr "Anfonwch ddiweddariadau ataf i drwy'r e-bost"
+#: templates/web/default/errors/page_error_410_gone.html:10
+#: templates/web/default/errors/page_error_410_gone.html:12
+msgid "The requested URL '%s' is no longer available"
+msgstr ""
-#: web-admin/index.cgi:156
-#, perl-format
-msgid "%d confirmed alerts, %d unconfirmed"
+#: templates/web/default/errors/page_error_404_not_found.html:10
+#: templates/web/default/errors/page_error_404_not_found.html:12
+msgid "The requested URL '%s' was not found on this server"
msgstr ""
-#: web-admin/index.cgi:157
-#, perl-format
-msgid "%d questionnaires sent &ndash; %d answered (%s%%)"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:479
+#: perllib/FixMyStreet/App/Controller/Admin.pm:649
+#: perllib/FixMyStreet/App/Controller/Admin.pm:688
+#: perllib/FixMyStreet/App/Controller/Admin.pm:773
+msgid "The requested URL was not found on this server."
msgstr ""
-#: web-admin/index.cgi:158
-#, perl-format
-msgid "%d council contacts &ndash; %d confirmed, %d unconfirmed"
+#: templates/web/default/alert/list.html:47
+msgid "The simplest alert is our geographic one:"
+msgstr "Yr hysbysiad symlaf yw ein hysbysiad daearyddol:"
+
+#: templates/web/default/report/new/all_councils_text.html:18
+#: templates/web/default/report/new/some_councils_text.html:10
+#: templates/web/default/report/new/some_councils_text.html:11
+msgid ""
+"The subject and details of the empty property will be public, plus your name "
+"if you give us permission."
msgstr ""
-#: web-admin/index.cgi:163
-msgid "Graph of empty property creation by status over time"
+#: bin/send-reports:77
+msgid ""
+"The user could not locate the empty property on a map, but to see the area "
+"around the location they entered"
msgstr ""
+"Ni allai'r defnyddiwr leoli'r eiddo gwag ar fap, ond i weld yr ardal o "
+"gwmpas y lleoliad a gofnodwyd ganddynt "
-#: web-admin/index.cgi:166
-#, fuzzy
-msgid "Empty property breakdown by state"
-msgstr "ni fydd eich eiddo gwag yn cael ei bostio"
+#: perllib/FixMyStreet/App/Controller/Contact.pm:115
+msgid "There were problems with your report. Please see below."
+msgstr "Cafwyd anhawsterau gyda'ch adroddiad. Gweler isod."
-#: web-admin/index.cgi:171
-msgid "Update breakdown by state"
+#: perllib/FixMyStreet/App/Controller/Report/Update.pm:202
+msgid "There were problems with your update. Please see below."
+msgstr "Cafwyd anhawsterau gyda'ch diweddariad. Gweler isod"
+
+#: bin/send-reports:175
+msgid ""
+"This email has been sent to both councils covering the location of the empty "
+"property, as the user did not categorise it; please ignore it if you're not "
+"the correct council to deal with the issue, or let us know what category of "
+"empty property this is so we can add it to our system."
msgstr ""
-#: web-admin/index.cgi:187
-msgid "Diligency prize league table"
+#: bin/send-reports:178
+msgid ""
+"This email has been sent to several councils covering the location of the "
+"empty property, as the category selected is provided for all of them; please "
+"ignore it if you're not the correct council to deal with the issue."
msgstr ""
-#: web-admin/index.cgi:191
-#, perl-format
-msgid "%d edits by %s"
+#: templates/web/default/debug_header.html:3
+#, fuzzy
+msgid ""
+"This is a developer site; things might break at any time, and the database "
+"will be periodically deleted."
+msgstr "Safle datblygwr yw hwn; gallai pethau dorri unrhyw bryd."
+
+#: templates/web/default/reports/council.html:20
+msgid "This is a summary of all reports for one %s."
+msgstr "Dyma grynodeb o'r holl adroddiadau ar gyfer y %s."
+
+#: templates/web/default/reports/council.html:22
+msgid "This is a summary of all reports for this %s."
msgstr ""
-#: web-admin/index.cgi:194
-msgid "No edits have yet been made."
+#: templates/web/default/reports/index.html:4
+#: templates/web/emptyhomes/reports/index.html:4
+#: templates/web/fiksgatami/reports/index.html:4
+msgid ""
+"This is a summary of all reports on this site; select a particular council "
+"to see the reports sent there."
msgstr ""
+"Dyma grynodeb o'r holl adroddiadau ar y safle hwn; dewiswch gyngor penodol i "
+"weld yr adroddiadau a anfonwyd ato."
-#: web-admin/index.cgi:198
-#, fuzzy
-msgid "Councils"
-msgstr "cyngor"
+#: perllib/FixMyStreet/Cobrand/Default.pm:815
+#: perllib/FixMyStreet/Cobrand/EmptyHomes.pm:117
+#: templates/web/default/report/display.html:111
+msgid "This empty property has been returned to use"
+msgstr "Mae'r eiddo gwag hwn wedi cael ei adfer i'w ddefnyddio"
-#: web-admin/index.cgi:212 web-admin/index.cgi:587
-#, fuzzy
-msgid "None"
-msgstr "Nac ydw"
+#: perllib/FixMyStreet/Cobrand/Default.pm:811
+msgid "This empty property is old and of unknown status."
+msgstr "Mae'r eiddo gwag hwn yn hen ac nid yw ei statws yn hysbys."
-#: web-admin/index.cgi:224
-#, fuzzy, perl-format
-msgid "%d addresses"
-msgstr "wythnos"
+#: perllib/FixMyStreet/DB/ResultSet/AlertType.pm:80
+#, fuzzy
+msgid "This report is currently marked as returned to use."
+msgstr "Mae'r eiddo gwag hwn wedi cael ei adfer i'w ddefnyddio"
-#: web-admin/index.cgi:230
-msgid "No info at all"
+#: perllib/FixMyStreet/DB/ResultSet/AlertType.pm:81
+msgid "This report is currently marked as open."
msgstr ""
-#: web-admin/index.cgi:232
-msgid "Currently has 1+ deleted"
+#: bin/send-reports:70
+msgid ""
+"This web page also contains a photo of the empty property, provided by the "
+"user."
msgstr ""
+"Mae'r dudalen we hon hefyd yn cynnwys ffotograff o'r eiddo gwag, wedi'i "
+"ddarparu gan y defnyddiwr."
-#: web-admin/index.cgi:234
-msgid "Some unconfirmeds"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:600
+#: templates/web/default/admin/timeline.html:1
+msgid "Timeline"
msgstr ""
-#: web-admin/index.cgi:236
-msgid "All confirmed"
+#: templates/web/default/admin/search_reports.html:13
+msgid "Title"
msgstr ""
-#: web-admin/index.cgi:266 web-admin/index.cgi:276 web-admin/index.cgi:293
-msgid "*unknown*"
+#: templates/web/default/alert/index.html:21
+msgid ""
+"To find out what local alerts we have in your area, council or ward, please "
+"enter your GB\n"
+"postcode or street name and area:"
msgstr ""
+"I gael gwybod pa hysbysiadau lleol sydd gennym yn eich ardal, cyngor neu "
+"ward chi,\n"
+"cofnodwch eich cod post Prydeinig neu enw stryd ac ardal:"
-#: web-admin/index.cgi:269 web-admin/index.cgi:297
+#: templates/web/default/around/display_location.html:92
#, fuzzy
-msgid "Values updated"
-msgstr "Anfonwch ddiweddariadau ataf i drwy'r e-bost"
+msgid ""
+"To report an empty property, simply <strong>click on the map</strong> at the "
+"correct location."
+msgstr ""
+"Er mwyn rhoi gwybod am eiddo gwag\n"
+" <strong>cliciwch ar y map</strong> wrth y lleoliad cywir."
-#: web-admin/index.cgi:279
-msgid "New category contact added"
+#: bin/send-reports:76
+msgid "To view a map of the precise location of this issue"
+msgstr "Gweld map o union leoliad y broblem hon"
+
+#: templates/web/default/maps/tilma/original.html:10
+msgid "Unable to fetch the map tiles from the tile server."
msgstr ""
-#: web-admin/index.cgi:316 web-admin/index.cgi:399
-#, perl-format
-msgid "Council contacts for %s"
+#: templates/web/default/admin/report_edit.html:22
+#: templates/web/default/admin/update_edit.html:19
+msgid "Unconfirmed"
msgstr ""
-#: web-admin/index.cgi:329
+#: perllib/FixMyStreet/App/Controller/Rss.pm:166
#, fuzzy
-msgid " List all reported empty properties"
-msgstr "Eiddo gwag yr adroddwyd amdanynt yn ddiweddar"
+msgid "Unknown alert type"
+msgstr "ID eiddo gwag anhysbys"
-#: web-admin/index.cgi:331
-msgid "Text only version"
-msgstr ""
+#: perllib/FixMyStreet/App/Controller/Report.pm:70
+#: perllib/FixMyStreet/App/Controller/Report/Update.pm:24
+#: perllib/FixMyStreet/App/Controller/Rss.pm:37
+msgid "Unknown empty property ID"
+msgstr "ID eiddo gwag anhysbys"
-#: web-admin/index.cgi:337 web-admin/index.cgi:478
-#, fuzzy
-msgid "Category"
-msgstr "Categori:"
+#: templates/web/default/admin/timeline.html:35
+msgid "Update %s created for empty property %d; by %s"
+msgstr ""
-#: web-admin/index.cgi:337 web-admin/index.cgi:439 web-admin/index.cgi:478
-#: web-admin/index.cgi:657
+#: templates/web/default/contact/index.html:21
#, fuzzy
-msgid "Email"
-msgstr "E-bost:"
+msgid "Update below added anonymously at %s"
+msgstr "Adroddwyd yn ddi-enw am %s"
-#: web-admin/index.cgi:337 web-admin/index.cgi:369 web-admin/index.cgi:422
-#: web-admin/index.cgi:439 web-admin/index.cgi:738
+#: templates/web/default/contact/index.html:23
#, fuzzy
-msgid "Confirmed"
-msgstr "Cadarnhau"
+msgid "Update below added by %s at %s"
+msgstr "Adroddwyd gan %s trwy %s am %s"
-#: web-admin/index.cgi:337 web-admin/index.cgi:371 web-admin/index.cgi:424
-#: web-admin/index.cgi:439
-msgid "Deleted"
+#: templates/web/default/admin/index.html:29
+msgid "Update breakdown by state"
msgstr ""
-#: web-admin/index.cgi:337
-msgid "Last editor"
-msgstr ""
+#: db/alert_types.pl:7
+msgid "Update by {{name}}"
+msgstr "Diweddariadau yn ôl {{name}}"
-#: web-admin/index.cgi:337 web-admin/index.cgi:439
+#: templates/web/default/admin/council_contacts.html:46
#, fuzzy
-msgid "Note"
-msgstr "Nac ydw"
+msgid "Update statuses"
+msgstr "Diweddariadau"
-#: web-admin/index.cgi:337 web-admin/index.cgi:439
-msgid "When edited"
-msgstr ""
+#: templates/web/default/report/display.html:103
+msgid "Update:"
+msgstr "Diweddariad:"
-#: web-admin/index.cgi:337
+#: perllib/FixMyStreet/App/Controller/Admin.pm:572
+#: perllib/FixMyStreet/App/Controller/Admin.pm:735
#, fuzzy
-msgid "Confirm"
-msgstr "Cadarnhau"
+msgid "Updated!"
+msgstr "Diweddariadau"
-#: web-admin/index.cgi:354
-#, fuzzy
-msgid "Update statuses"
+#: templates/web/default/admin/list_updates.html:1
+#: templates/web/default/report/updates.html:4
+msgid "Updates"
msgstr "Diweddariadau"
-#: web-admin/index.cgi:359
-msgid "Add new category"
-msgstr ""
+#: db/alert_types.pl:5 db/alert_types.pl:6
+msgid "Updates on {{title}}"
+msgstr "Diweddariadau am {{title}}"
+
+#: templates/web/default/report/display.html:0
+#: templates/web/default/report/display.html:7
+msgid "Updates to this empty property, reportemptyhomes.com"
+msgstr "Diweddariadau i'r eiddo gwag hwn, reportemptyhomes.com"
-#: web-admin/index.cgi:362 web-admin/index.cgi:416
+#: perllib/FixMyStreet/App/Controller/Admin.pm:286
+#: perllib/FixMyStreet/App/Controller/Admin.pm:316
#, fuzzy
-msgid "Category: "
-msgstr "Categori:"
+msgid "Values updated"
+msgstr "Anfonwch ddiweddariadau ataf i drwy'r e-bost"
-#: web-admin/index.cgi:365 web-admin/index.cgi:419
+#: templates/web/default/admin/report_edit.html:12
+#: templates/web/default/admin/update_edit.html:12
#, fuzzy
-msgid "Email: "
-msgstr "E-bost:"
+msgid "View report on site"
+msgstr "Adroddiadau"
-#: web-admin/index.cgi:374 web-admin/index.cgi:427
-msgid "Note: "
-msgstr ""
+#: templates/web/emptyhomes/tokens/confirm_problem.html:39
+msgid "View your report"
+msgstr "Gweld eich adroddiad"
-#: web-admin/index.cgi:381
-#, fuzzy
-msgid "Create category"
-msgstr "Categori:"
+#: templates/web/default/around/display_location.html:0
+#: templates/web/default/around/display_location.html:34
+msgid "Viewing a location"
+msgstr "Gweld lleoliad"
-#: web-admin/index.cgi:433
-msgid "Save changes"
-msgstr ""
+#: templates/web/default/report/display.html:0
+msgid "Viewing an empty property"
+msgstr "Gweld eiddo gwag"
-#: web-admin/index.cgi:437
-msgid "History"
+#: perllib/FixMyStreet/Geocode.pm:155 perllib/FixMyStreet/Geocode.pm:174
+msgid ""
+"We do not cover Northern Ireland, I'm afraid, as our licence doesn't include "
+"any maps for the region."
msgstr ""
+"Nid ydym yn ymdrin â Gogledd Iwerddon, yn anffodus, gan nad yw ein trwydded "
+"yn cynnwys unrhyw fapiau ar gyfer y rhanbarth."
-#: web-admin/index.cgi:439
-msgid "Editor"
+#: templates/web/default/alert/choose.html:6
+#: templates/web/default/around/around_index.html:33
+msgid ""
+"We found more than one match for that location. We show up to ten matches, "
+"please try a different search if yours is not here."
msgstr ""
+"Darganfuom fwy nag un man sy'n cyfateb i'r lleoliad hwnnw. Rydym yn dangos "
+"hyd at ddeg cyfatebiaeth, rhowch gynnig ar chwiliad gwahanol os nad yw'ch "
+"lleoliad chi yno."
-#: web-admin/index.cgi:443 web-admin/index.cgi:444
-#, fuzzy
-msgid "yes"
-msgstr "Ydw"
+#: perllib/FixMyStreet/App/Controller/Around.pm:235
+msgid ""
+"We had an empty property with the supplied co-ordinates - outside the UK?"
+msgstr ""
-#: web-admin/index.cgi:443 web-admin/index.cgi:444
-msgid "no"
+#: templates/web/emptyhomes/tokens/confirm_problem.html:18
+#: templates/web/emptyhomes/tokens/confirm_problem.html:20
+msgid ""
+"We may contact you periodically to ask if anything has changed with the "
+"property you reported."
msgstr ""
-#: web-admin/index.cgi:471
-msgid "Search:"
+#: bin/send-reports:185
+msgid ""
+"We realise this empty property might be the responsibility of %s; however, "
+"we don't currently have any contact details for them. If you know of an "
+"appropriate contact address, please do get in touch."
msgstr ""
-#: web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "ID"
+#: templates/web/default/index.html:62
+msgid ""
+"The details will be sent directly to the right person in the local council "
+"for them to take action"
msgstr ""
+"Caiff y manylion eu hanfon yn uniongyrchol at yr unigolyn cywir yn y cyngor "
+"lleol er mwyn iddynt weithredu"
-#: web-admin/index.cgi:478
-msgid "Title"
+#: templates/web/default/report/new/notes.html:5
+msgid ""
+"We will only use your personal information in accordance with our <a href=\"/"
+"faq#privacy\">privacy policy.</a>"
msgstr ""
-#: web-admin/index.cgi:478
+#: templates/web/emptyhomes/contact/blurb.html:2
#, fuzzy
-msgid "Council"
-msgstr "cyngor"
-
-#: web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "Cobrand"
+msgid ""
+"We&rsquo;d love to hear what you think about this website. Just fill in the "
+"form. Please don&rsquo;t contact us about individual empty homes; use the "
+"box accessed from <a href=\"/\">the front page</a>."
msgstr ""
+"Byddem wrth ein bodd yn clywed eich barn am y wefan hon.\n"
+"Dim ond llenwi'r ffurflen sydd angen i chi ei wneud. Peidiwch â defnyddio "
+"hwn i gysylltu â ni am dai gwag unigol;\n"
+"defnyddiwch y blwch sydd i'w gael ar y <a href=\"/\">dudalen flaen</a>."
-#: web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "Created"
+#: templates/web/default/contact/blurb.html:8
+msgid ""
+"We'd love to hear what you think about this site. Just fill in the form, or "
+"send an email to <a href='mailto:%s'>%s</a>:"
msgstr ""
+"Byddai'n dda gennym glywed eich barn am y wefan hon. Llenwch y ffurflen, neu "
+"anfonwch neges e-bost at <a href='mailto:%s'>%s</a>:"
-#: web-admin/index.cgi:478 web-admin/index.cgi:657
-#, fuzzy
-msgid "State"
-msgstr "Diweddariad:"
+#: templates/web/default/admin/council_contacts.html:25
+#: templates/web/default/admin/council_edit.html:41
+msgid "When edited"
+msgstr ""
-#: web-admin/index.cgi:478
+#: templates/web/default/admin/search_reports.html:22
msgid "When sent"
msgstr ""
-#: web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "*"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:464
+msgid "Whole block of empty flats"
+msgstr "Bloc cyfan o fflatiau gwag"
+
+#: templates/web/default/questionnaire/index.html:104
+msgid ""
+"Would you like to receive another questionnaire in 4 weeks, reminding you to "
+"check the status?"
msgstr ""
-#: web-admin/index.cgi:500 web-admin/index.cgi:634
-#, fuzzy
-msgid "Confirmed:"
-msgstr "Cadarnhau"
+#: templates/web/default/report/new/notes.html:8
+msgid ""
+"Writing your message entirely in block capitals makes it hard to read, as "
+"does a lack of punctuation."
+msgstr ""
-#: web-admin/index.cgi:501
-msgid "Fixed:"
+#: templates/web/default/admin/council_contacts.html:32
+#: templates/web/default/admin/council_contacts.html:33
+#: templates/web/default/admin/council_edit.html:5
+#: templates/web/default/admin/list_updates.html:28
+#: templates/web/default/admin/report_edit.html:18
+#: templates/web/default/admin/report_edit.html:37
+#: templates/web/default/admin/search_reports.html:43
+#: templates/web/default/admin/update_edit.html:15
+#: templates/web/default/questionnaire/creator_fixed.html:14
+#: templates/web/default/questionnaire/index.html:107
+#: templates/web/default/questionnaire/index.html:68
+msgid "Yes"
+msgstr "Ydw"
+
+#: templates/web/default/contact/index.html:37
+msgid ""
+"You are reporting the following empty property report for being abusive, "
+"containing personal information, or similar:"
msgstr ""
+"Rydych yn cwyno am yr adroddiad canlynol am eiddo gwag gan ei fod yn "
+"sarhaus, neu'n cynnwys gwybodaeth bersonol, neu debyg:"
-#: web-admin/index.cgi:502
-msgid "Last&nbsp;update:"
+#: templates/web/default/contact/index.html:15
+msgid ""
+"You are reporting the following update for being abusive, containing "
+"personal information, or similar:"
msgstr ""
-#: web-admin/index.cgi:518 web-admin/index.cgi:676
-msgid "Edit"
+#: templates/web/default/reports/council.html:35
+msgid ""
+"You can <a href=\"%s\">see less detail</a> or go back and <a href=\"/reports"
+"\">show all councils</a>."
msgstr ""
+"Gallwch <a href=\"%s\">weld llai o fanylion</a> neu fynd yn ôl a <a href=\"/"
+"reports\">dangos pob cyngor</a>."
-#: web-admin/index.cgi:543
-#, fuzzy
-msgid "That empty property will now be resent."
-msgstr "ni fydd eich eiddo gwag yn cael ei bostio"
+#: templates/web/default/reports/council.html:31
+msgid "You can <a href=\"%s\">see less detail</a>."
+msgstr "Gallwch <a href=\"%s\">weld llai o fanylion</a>."
-#: web-admin/index.cgi:549
-msgid "I am afraid you cannot confirm unconfirmed reports."
+#: templates/web/default/reports/council.html:37
+msgid ""
+"You can <a href=\"%s\">see more details</a> or go back and <a href=\"/reports"
+"\">show all councils</a>."
msgstr ""
+"Gallwch <a href=\"%s\">weld mwy o fanylion</a> neu fynd yn ôl a <a href=\"/"
+"reports\">dangos pob cyngor</a>."
-#: web-admin/index.cgi:578 web-admin/index.cgi:698
-#, fuzzy
-msgid "Updated!"
-msgstr "Diweddariadau"
+#: templates/web/default/reports/council.html:33
+msgid "You can <a href=\"%s\">see more details</a>."
+msgstr "Gallwch <a href=\"%s\">weld mwy o fanylion</a>."
-#: web-admin/index.cgi:582
-#, fuzzy, perl-format
-msgid "Editing empty property %d"
-msgstr "Gweld eiddo gwag"
+#: templates/web/default/report/new/no_councils_text.html:14
+#: templates/web/default/report/new/no_councils_text.html:3
+#: templates/web/default/report/new/some_councils_text.html:20
+#: templates/web/default/report/new/some_councils_text.html:22
+msgid ""
+"You can help us by finding a contact email address for local empty "
+"properties for %s and emailing it to us at <a href='mailto:%s'>%s</a>."
+msgstr ""
-#: web-admin/index.cgi:592
-msgid "used map"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:42
+msgid ""
+"You have already answered this questionnaire. If you have a question, please "
+"<a href='%s'>get in touch</a>, or <a href='%s'>view your empty property</"
+"a>.\n"
msgstr ""
+"Rydych eisoes wedi ateb yr holiadur hwn. Os oes gennych gwestiwn, <a href='%"
+"s'>cysylltwch â ni</a>, neu ewch i <a href='%s'>weld eich eiddo gwag</a>.\n"
-#: web-admin/index.cgi:592
-msgid "didn't use map"
+#: templates/web/default/questionnaire/index.html:94
+#: templates/web/default/report/display.html:122
+#: templates/web/default/report/new/fill_in_details.html:111
+msgid ""
+"You have already attached a photo to this report, attaching another one will "
+"replace it."
msgstr ""
-#: web-admin/index.cgi:604 web-admin/index.cgi:734
-msgid "Remove photo (can't be undone!)"
+#: templates/web/default/auth/logout.html:3
+msgid "You have been logged out"
msgstr ""
-#: web-admin/index.cgi:609
+#: templates/web/default/report/new/fill_in_details.html:36
#, fuzzy
-msgid "Anonymous:"
-msgstr "Di-enw"
+msgid ""
+"You have located the empty property at the point marked with a purple pin on "
+"the map. If this is not the correct location, simply click on the map again. "
+msgstr ""
+"Rydych wedi gosod lleoliad yr eiddo gwag wrth y pwynt sy'n cael ei ddangos "
+"gan y pin porffor ar y map.\n"
+"Os nad dyma'r lleoliad cywir, cliciwch ar y map eto. "
-#: web-admin/index.cgi:610 web-admin/index.cgi:738
-#, fuzzy
-msgid "State:"
-msgstr "Diweddariad:"
+#: templates/web/default/tokens/confirm_alert.html:7
+msgid "You have successfully confirmed your alert."
+msgstr "Rydych wedi cadarnhau'ch hysbysiad yn llwyddiannus."
-#: web-admin/index.cgi:610
-msgid "Open"
-msgstr ""
+#: templates/web/default/tokens/confirm_problem.html:6
+#: templates/web/default/tokens/confirm_problem.html:7
+msgid "You have successfully confirmed your empty property"
+msgstr "Rydych wedi cadarnhau eich eiddo gwag yn llwyddiannus"
-#: web-admin/index.cgi:610
-msgid "Fixed"
+#: templates/web/default/tokens/confirm_update.html:11
+#: templates/web/default/tokens/confirm_update.html:12
+msgid ""
+"You have successfully confirmed your update and you can now <a href=\"%s"
+"\">view it on the site</a>."
msgstr ""
+"Rydych wedi cadarnhau eich diweddariad yn llwyddiannus ac nawr gallwch <a "
+"href=\"%s\">weld hwn ar y safle</a>."
-#: web-admin/index.cgi:610 web-admin/index.cgi:738
+#: templates/web/default/tokens/confirm_alert.html:11
#, fuzzy
-msgid "Hidden"
-msgstr "Cuddio pinnau"
+msgid "You have successfully created your alert."
+msgstr "Rydych wedi dileu'ch hysbysiad yn llwyddiannus."
-#: web-admin/index.cgi:610 web-admin/index.cgi:738
-msgid "Unconfirmed"
-msgstr ""
+#: templates/web/default/tokens/confirm_alert.html:9
+msgid "You have successfully deleted your alert."
+msgstr "Rydych wedi dileu'ch hysbysiad yn llwyddiannus."
-#: web-admin/index.cgi:610
-msgid "Partial"
+#: templates/web/default/email_sent.html:30
+msgid ""
+"You must now click the link in the email we've just sent you &mdash; if you "
+"do not, %s."
msgstr ""
-#: web-admin/index.cgi:613
+#: templates/web/default/admin/report_edit.html:32
msgid "You really want to resend?"
msgstr ""
-#: web-admin/index.cgi:613
-#, fuzzy
-msgid "Resend report"
-msgstr "Cuddio hen adroddiadau "
-
-#: web-admin/index.cgi:622
+#: templates/web/default/my/my.html:3
#, fuzzy
-msgid "View report on site"
+msgid "Your Reports"
msgstr "Adroddiadau"
-#: web-admin/index.cgi:625
-msgid "Co-ordinates:"
-msgstr ""
+#: templates/web/default/alert/list.html:123
+msgid "Your email:"
+msgstr "Eich cyfeiriad e-bost:"
-#: web-admin/index.cgi:625
-msgid "originally entered"
-msgstr ""
+#: templates/web/default/contact/index.html:68
+msgid "Your name:"
+msgstr "Eich enw:"
-#: web-admin/index.cgi:626
-#, fuzzy
-msgid "For council(s):"
-msgstr "cyngor"
+#: templates/web/default/contact/index.html:76
+msgid "Your&nbsp;email:"
+msgstr "Eich&nbsp;cyfeiriad e-bost:"
-#: web-admin/index.cgi:626
-msgid "other areas:"
+#: templates/web/default/admin/timeline.html:6
+msgid "by %s"
msgstr ""
-#: web-admin/index.cgi:633 web-admin/index.cgi:754
-msgid "Created:"
+#: templates/web/default/reports/council.html:6
+#: templates/web/default/reports/council.html:7
+msgid "council"
+msgstr "cyngor"
+
+#: templates/web/default/admin/report_edit.html:15
+msgid "didn't use map"
msgstr ""
-#: web-admin/index.cgi:635
-#, fuzzy
-msgid "Sent:"
-msgstr "Pwnc:"
+#: perllib/Utils.pm:241
+msgid "less than a minute"
+msgstr "llai na munud"
-#: web-admin/index.cgi:636
-#, fuzzy
-msgid "Last update:"
-msgstr "Anfonwch ddiweddariadau ataf i drwy'r e-bost"
+#: templates/web/default/report/updates.html:13
+msgid "marked as returned to use"
+msgstr "cofnodwyd bod hyn wedi'i adfer i'w ddefnyddio"
-#: web-admin/index.cgi:637
-msgid "Service:"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:107
+#: templates/web/default/admin/questionnaire.html:15
+#: templates/web/default/admin/questionnaire.html:16
+msgid "n/a"
msgstr ""
-#: web-admin/index.cgi:638 web-admin/index.cgi:752
-msgid "Cobrand:"
-msgstr ""
+#: templates/web/default/alert/list.html:119
+msgid "or"
+msgstr "neu"
-#: web-admin/index.cgi:639 web-admin/index.cgi:753
-msgid "Cobrand data:"
+#: templates/web/default/admin/report_edit.html:15
+msgid "originally entered"
msgstr ""
-#: web-admin/index.cgi:640
-#, fuzzy
-msgid "Going to send questionnaire?"
-msgstr "Anfon yr holiadur"
+#: templates/web/default/admin/report_edit.html:16
+msgid "other areas:"
+msgstr ""
-#: web-admin/index.cgi:644 web-admin/index.cgi:758
-#, fuzzy
-msgid "Submit changes"
-msgstr "Anfon"
+#: templates/web/default/report/updates.html:14
+msgid "reopened"
+msgstr "wedi'i ailagor"
-#: web-admin/index.cgi:657
-msgid "Text"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:361
+msgid "the map was not used so pin location may be inaccurate"
msgstr ""
+"ni ddefnyddiwyd y map felly mae'n bosibl na fydd lleoliad y pin yn gywir"
-#: web-admin/index.cgi:703
+#: bin/send-reports:166
#, fuzzy
-msgid "Empty property marked as open."
-msgstr "Ffurflen manylion eiddo gwag"
-
-#: web-admin/index.cgi:719
-#, fuzzy, perl-format
-msgid "Editing update %d"
-msgstr "Cyflwyno'ch diweddariad"
+msgid "this type of local empty property"
+msgstr "Anfonwch fanylion eiddo gwag lleol newydd ataf i drwy'r e-bost"
-#: web-admin/index.cgi:747
-#, fuzzy
-msgid "View update on site"
-msgstr "Diweddariadau am {{title}}"
+#: perllib/Utils.pm:217
+msgid "today"
+msgstr "heddiw"
-#: web-admin/index.cgi:748
-msgid "Text:"
+#: templates/web/default/admin/report_edit.html:15
+msgid "used map"
msgstr ""
-#: web-admin/index.cgi:750
-msgid "(blank to go anonymous)"
+#: templates/web/default/reports/council.html:0
+#: templates/web/default/reports/council.html:3
+msgid "ward"
msgstr ""
-#: web-admin/index.cgi:832 web-admin/index.cgi:835
-#, perl-format
-msgid "by %s"
+#: templates/web/default/email_sent.html:15
+#: templates/web/default/email_sent.html:3
+msgid "we'll hang on to your alert while you're checking your email."
+msgstr "byddwn yn dal gafael ar eich hysbysiad wrth i chi wirio eich e-bost."
+
+#: templates/web/default/email_sent.html:3
+#: templates/web/default/email_sent.html:7
+msgid ""
+"we'll hang on to your empty property report while you're checking your email."
msgstr ""
+"byddwn yn dal gafael ar eich hysbysiad am eiddo gwag wrth i chi wirio eich e-"
+"bost."
-#: web-admin/index.cgi:833
-#, fuzzy, perl-format
-msgid "Empty property %d created"
-msgstr "Eiddo gwag"
+#: templates/web/default/email_sent.html:11
+#: templates/web/default/email_sent.html:3
+msgid "we'll hang on to your update while you're checking your email."
+msgstr "byddwn yn dal gafael ar eich diweddariad wrth i chi wirio eich e-bost."
-#: web-admin/index.cgi:838
-#, fuzzy, perl-format
-msgid "Empty property %s confirmed"
-msgstr "Ffurflen manylion eiddo gwag"
+#: templates/web/default/email_sent.html:14
+#: templates/web/default/email_sent.html:3
+msgid "your alert will not be activated"
+msgstr "ni fydd eich hysbysiad yn cael ei weithredu"
-#: web-admin/index.cgi:842
-#, fuzzy, perl-format
-msgid "Empty property %s sent to council %s"
-msgstr "Eiddo gwag yn %s"
+#: templates/web/default/email_sent.html:3
+#: templates/web/default/email_sent.html:6
+msgid "your empty property will not be posted"
+msgstr "ni fydd eich eiddo gwag yn cael ei bostio"
-#: web-admin/index.cgi:844
-#, fuzzy, perl-format
-msgid "Questionnaire %d sent for empty property %d"
-msgstr "Llenwyd yr holiadur gan yr unigolyn a roddodd wybod am yr eiddo gwag"
+#: templates/web/default/email_sent.html:10
+#: templates/web/default/email_sent.html:3
+msgid "your update will not be posted"
+msgstr "ni fydd eich diweddariad yn cael ei bostio"
-#: web-admin/index.cgi:846
-#, fuzzy, perl-format
-msgid "Questionnaire %d answered for empty property %d, %s to %s"
-msgstr "Llenwyd yr holiadur gan yr unigolyn a roddodd wybod am yr eiddo gwag"
+#: templates/web/default/front_stats.html:19
+#, perl-format
+msgid "<big>%s</big> report recently"
+msgid_plural "<big>%s</big> reports recently"
+msgstr[0] "<big>%s</big> adroddiad yn ddiweddar"
+msgstr[1] "<big>%s</big> o adroddiadau yn ddiweddar"
-#: web-admin/index.cgi:851
+#: templates/web/default/report/new/no_councils_text.html:5
#, perl-format
-msgid "Update %s created for empty property %d; by %s"
-msgstr ""
+msgid "We do not yet have details for the council that covers this location."
+msgid_plural ""
+"We do not yet have details for the councils that cover this location."
+msgstr[0] ""
+msgstr[1] ""
-#: web-admin/index.cgi:855
+#: templates/web/default/front_stats.html:14
#, perl-format
-msgid "Alert %d created for %s, type %s, parameters %s / %s"
-msgstr ""
+msgid "<big>%s</big> report in past week"
+msgid_plural "<big>%s</big> reports in past week"
+msgstr[0] "<big>%s</big> adroddiad o fewn yr wythnos ddiwethaf"
+msgstr[1] "<big>%s</big> o adroddiadau o fewn yr wythnos ddiwethaf"
-#: web-admin/index.cgi:858
+#: templates/web/default/front_stats.html:25
#, perl-format
-msgid "Alert %d disabled (created %s)"
-msgstr ""
+msgid "<big>%s</big> returned to use in past month"
+msgid_plural "<big>%s</big> returned to use in past month"
+msgstr[0] "Dychwelwyd <big>%s</big> i'w ddefnyddio yn y mis diwethaf"
+msgstr[1] "Dychwelwyd <big>%s</big> i'w defnyddio yn y mis diwethaf"
+
+#: templates/web/default/report/new/some_councils_text.html:14
+#, perl-format
+msgid ""
+"We do <strong>not</strong> yet have details for the other council that "
+"covers this location."
+msgid_plural ""
+"We do <strong>not</strong> yet have details for the other councils that "
+"cover this location."
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/web/default/front_stats.html:31
+#, perl-format
+msgid "<big>%s</big> update on reports"
+msgid_plural "<big>%s</big> updates on reports"
+msgstr[0] "<big>%s</big> diweddariad ar adroddiadau"
+msgstr[1] "<big>%s</big> diweddariad ar adroddiadau"
+
+#~ msgid "Sorry! Something's gone wrong."
+#~ msgstr "Ymddiheuriadau! Mae rhywbeth wedi mynd o'i le."
+
+#~ msgid "The text of the error was:"
+#~ msgstr "Testun y gwall oedd:"
+
+#~ msgid ""
+#~ "<h1>Nearly Done! Now check your email...</h1>\n"
+#~ "<p>The confirmation email <strong>may</strong> take a few minutes to "
+#~ "arrive &mdash; <em>please</em> be patient.</p>\n"
+#~ "<p>If you use web-based email or have 'junk mail' filters, you may wish "
+#~ "to check your bulk/spam mail folders: sometimes, our messages are marked "
+#~ "that way.</p>\n"
+#~ "<p>You must now click the link in the email we've just sent you &mdash;\n"
+#~ "if you do not, %s.</p>\n"
+#~ "<p>(Don't worry &mdash; %s)</p>\n"
+#~ msgstr ""
+#~ "<h1>Bron â gorffen! Nawr, gwiriwch eich e-bost...</h1>\n"
+#~ "<p> <strong>Gallai'r</strong> e-bost o gadarnhad gymryd rhai munudau i "
+#~ "gyrraedd &mdash; <em>byddwch</em> yn amyneddgar.</p>\n"
+#~ "<p>Os ydych yn defnyddio system e-bost ar y we neu os oes gennych hidlydd "
+#~ "'negeseuon sgrwtsh', efallai y byddwch am wirio eich ffolderi post sbam/"
+#~ "swmp: weithiau, caiff ein negeseuon eu marcio fel hynny.</p>\n"
+#~ "<p>Nawr, mae'n rhaid i chi glicio'r ddolen yn yr e-bost yr ydym newydd ei "
+#~ "anfon atoch &mdash;\n"
+#~ "os nad ydych, %s.</p>\n"
+#~ "<p>(Peidiwch â phoeni &mdash; %s)</p>\n"
+
+#~ msgid ""
+#~ "Thank you for trying to confirm your alert. We seem to have an error "
+#~ "ourselves\n"
+#~ "though, so <a href=\"%s\">please let us know what went on</a> and we'll "
+#~ "look into it.\n"
+#~ msgstr ""
+#~ "Diolch am geisio cadarnhau eich rhybudd. Fodd bynnag, mae'n ymddangos bod "
+#~ "gennym wall\n"
+#~ "ein hunain, felly <a href=\"%s\">rhowch wybod i ni beth sydd wedi "
+#~ "digwydd</a> ac fe wnawn ni edrych yn fanylach ar hyn.\n"
+
+#~ msgid "Local RSS feeds and email alerts for &lsquo;%s&rsquo;"
+#~ msgstr ""
+#~ "Porthiannau RSS lleol a hysbysiadau e-bost ar gyfer &lsquo;%s&rsquo;"
-#: web-admin/index.cgi:890
#, fuzzy
-msgid "Not reported before"
-msgstr "Wedi adrodd o'r blaen"
+#~ msgid "Receive alerts on new local empty properties"
+#~ msgstr "Anfonwch fanylion eiddo gwag lleol newydd ataf i drwy'r e-bost"
-#. Please leave the first word "Subject:" untranslated
-#: templates/emails/alert-confirm
-msgid ""
-"Subject: Confirm your alert on reportemptyhomes.com\n"
-"\n"
-"Hi,\n"
-"\n"
-"Please click on the link below to confirm the alert you just\n"
-"asked to subscribe to on reportemptyhomes.com:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"If you can't click the link, please copy and paste it to the\n"
-"address bar of your web browser.\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-msgstr ""
-"Subject: Cadarnhau eich rhybudd ar reportemptyhomes.com\n"
-"\n"
-"Helo,\n"
-"\n"
-"Cliciwch ar y ddolen isod i gadarnhau'r rhybudd yr ydych newydd\n"
-"ofyn am danysgrifio iddo ar Asiantaeth Tai Gwag:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"Os na allwch glicio ar y ddolen, dylech ei chopïo a'i gludo ym\n"
-"mar cyfeiriad eich porwr gwe.\n"
-"\n"
-"Yn gywir, \n"
-"Tîm yr Asiantaeth Tai Gwag\n"
-
-#: templates/emails/alert-problem
-msgid ""
-"Subject: New empty property reports on reportemptyhomes.com\n"
-"\n"
-"The following new empty properties have been added:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-"\n"
-"To stop receiving emails when there are new empty properties,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/alert-problem-area
-msgid ""
-"Subject: New empty property reports in <?=$values['area_name']?> on "
-"reportemptyhomes.com\n"
-"\n"
-"The following new empty properties have been added within\n"
-"<?=$values['area_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-"\n"
-"To stop receiving emails when there are new empty properties in\n"
-"<?=$values['area_name']?>, please follow this link: \n"
-"<?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/alert-problem-council
-msgid ""
-"Subject: New empty property reports reported to <?=$values['area_name']?> on "
-"reportemptyhomes.com\n"
-"\n"
-"The following new empty properties have been reported to <?=$values"
-"['area_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-"\n"
-"To stop receiving emails when there are new empty properties reported to\n"
-"<?=$values['area_name']?>, please follow this link: \n"
-"<?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/alert-problem-nearby
-msgid ""
-"Subject: New nearby empty properties on reportemptyhomes.com\n"
-"\n"
-"The following nearby empty properties have been added:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-"\n"
-"To stop receiving emails when there are nearby empty properties,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/alert-problem-ward
-msgid ""
-"Subject: New empty property reports reported to <?=$values['area_name']?> "
-"within <?=$values['ward_name']?> on reportemptyhomes.com\n"
-"\n"
-"The following new empty properties have been reported to <?=$values"
-"['area_name']?>\n"
-"within <?=$values['ward_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-"\n"
-"To stop receiving emails when there are new empty properties reported to\n"
-"<?=$values['area_name']?> within <?=$values['ward_name']?>,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/alert-update
-#, fuzzy
-msgid ""
-"Subject: New updates on empty property - '<?=$values['title']?>'\n"
-"\n"
-"The following updates have been left on this empty property:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"<?=$values['state_message']?>\n"
-"\n"
-"To view or reply to these updates, please visit the following URL:\n"
-" <?=$values['problem_url']?>\n"
-"\n"
-"You cannot contact anyone by replying to this email.\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-"\n"
-"To stop receiving emails when there are new updates on this empty property,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-"Subject: Diweddariadau newydd am eiddo gwag - '<?=$values['title']?>'\n"
-"\n"
-"Mae'r diweddariadau canlynol wedi cael eu gadael am yr eiddo gwag hwn:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Gweld y diweddariadau hyn neu eu hateb: <?=$values['problem_url']?>\n"
-"\n"
-"Yn gywir, \n"
-"reportemptyhomes.com\n"
-"\n"
-"I roi'r gorau i dderbyn negeseuon e-bost pan fydd diweddariadau newydd yn "
-"ymddangos am yr eiddo hwn,\n"
-"dilynwch y ddolen hon: <?=$values['unsubscribe_url']?>\n"
-
-#: templates/emails/empty property-confirm
-msgid ""
-"Subject: Confirm your empty property report\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"Please click on the link below to confirm the empty property\n"
-"report you just added to the site:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"If your email program does not let you click on this link,\n"
-"copy and paste it into your web browser and press return.\n"
-"\n"
-"Your report had the subject:\n"
-"<?=$values['title']?>\n"
-"\n"
-"And details:\n"
-"<?=$values['detail']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-msgstr ""
-"Subject: Cadarnhau eich adroddiad am eiddo gwag\n"
-"\n"
-"Helo <?=$values['name']?>,\n"
-"\n"
-"Cliciwch ar y ddolen isod i gadarnhau'r adroddiad am eiddo gwag\n"
-"yr ydych newydd ei ychwanegu at y safle:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"Os nad yw eich rhaglen e-bost yn gadael i chi glicio ar y ddolen hon,\n"
-"dylech ei chopïo a'i gludo i'ch porwr gwe a phwyso'r fysell 'return'.\n"
-"\n"
-"Roedd gan eich adroddiad y pwnc:\n"
-"<?=$values['title']?>\n"
-"\n"
-"A'r manylion:\n"
-"<?=$values['detail']?>\n"
-"\n"
-"Yn gywir, \n"
-"reportemptyhomes.com\n"
-
-#: templates/emails/flickr-confirm
-msgid ""
-"Subject: Confirm your email address on reportemptyhomes.com\n"
-"\n"
-"Hi,\n"
-"\n"
-"Please click on the link below to confirm the email address\n"
-"you just gave to reportemptyhomes.com:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"This is so we can look up the photos you tag with reportemptyhomes.com,\n"
-"and send you an email letting you know about your new empty properties.\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-msgstr ""
-
-#: templates/emails/flickr-submit
-msgid ""
-"Subject: New photo pulled from Flickr to reportemptyhomes.com\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"We've fetched a photo you uploaded to Flickr and tagged with\n"
-"reportemptyhomes.com. To check the details we have, and to add any more,\n"
-"please visit the following URL:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"Then we can send your photo to the council. Thanks!\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-msgstr ""
-
-#: templates/emails/partial
-#, fuzzy
-msgid ""
-"Subject: Confirm your report on reportemptyhomes.com\n"
-"\n"
-"Hi<?=$values['name']?>,\n"
-"\n"
-"To confirm the report you have uploaded to reportemptyhomes.com via\n"
-"<?=$values['service']?>, and to check or add any details,\n"
-"please visit the following URL:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"Thanks!\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-msgstr ""
-"Subject: Eich adroddiad newydd ar reportemptyhomes.com\n"
-"\n"
-"Helo<?=$values['name']?>,\n"
-"\n"
-"Rydym wedi storio'r adroddiad y gwnaethoch ei lwytho i'r Asiantaeth Tai Gwag "
-"drwy gyfrwng\n"
-"<?=$values['service']?>. I gadarnhau'r manylion sydd gennym,\n"
-"ac i ychwanegu atynt, ewch i'r URL canlynol:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"Yna, gallwn anfon eich adroddiad at y cyngor. Diolch!\n"
-"\n"
-"Yn gywir, \n"
-"Tîm yr Asiantaeth Tai Gwag\n"
-
-#: templates/emails/problem-confirm
-msgid ""
-"Subject: Confirm your empty property on reportemptyhomes.com\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"Please click on the link below to confirm the empty property you just\n"
-"added to reportemptyhomes.com:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"If your email program does not let you click on this link,\n"
-"copy and paste it into your web browser and press return.\n"
-"\n"
-"Your empty property had the title:\n"
-"<?=$values['title']?>\n"
-"\n"
-"And details:\n"
-"<?=$values['detail']?>\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-msgstr ""
-
-#: templates/emails/questionnaire
-msgid ""
-"Subject: Questionnaire about your empty property on reportemptyhomes.com\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"<?=$values['created']?> ago, you left an empty property on reportemptyhomes."
-"com\n"
-"with the details provided at the end of this email. To keep our\n"
-"site up to date and relevant, we'd appreciate it if you could\n"
-"follow the link below and fill in our short questionnaire\n"
-"updating the status of your empty property:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"Please do not reply to this email; there is a public comment\n"
-"box on the questionnaire.\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-"\n"
-"Your empty property was as follows:\n"
-"\n"
-"<?=$values['title']?>\n"
-"\n"
-"<?=$values['detail']?>\n"
-"\n"
-msgstr ""
-
-#: templates/emails/questionnaire-eha-26weeks
-msgid ""
-"Subject: Questionnaire about your empty property report\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"Six months ago, you reported an empty home on ReportEmptyHomes.com with the\n"
-"details provided at the end of this email. To keep our site up to date and\n"
-"relevant, I'd be grateful if you could fill in this short questionnaire to "
-"tell\n"
-"us what has happened: \n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"Please do not reply to this email; there is a public comment\n"
-"box on the questionnaire.\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"Your report was as follows:\n"
-"\n"
-"<?=$values['title']?>\n"
-"\n"
-"Property type: <?=$values['category']?>\n"
-"\n"
-"<?=$values['detail']?>\n"
-"\n"
-msgstr ""
-"Subject: Holiadur ar eich adroddiad am eiddo gwag\n"
-"\n"
-"Helo <?=$values['name']?>,\n"
-"\n"
-"Chwe mis yn ôl, fe wnaethoch adrodd am eiddo gwag ar ReportEmptyHomes.com "
-"gan roi'r\n"
-"manylion sydd wedi'u cynnwys ar ddiwedd y neges e-bost hon. Er mwyn cadw'n "
-"safle'n gyfredol\n"
-"ac yn berthnasol, byddwn yn ddiolchgar petaech yn llenwi'r holiadur byr hwn "
-"er mwyn\n"
-"dweud wrthom ni beth sydd wedi digwydd: \n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"Peidiwch ag ateb y neges hon; mae blwch sylwadau cyhoeddus\n"
-"ar yr holiadur.\n"
-"\n"
-"Yn gywir, \n"
-"reportemptyhomes.com\n"
-"\n"
-"Roedd eich adroddiad fel a ganlyn:\n"
-"\n"
-"<?=$values['title']?>\n"
-"\n"
-"Math o eiddo: <?=$values['category']?>\n"
-"\n"
-"<?=$values['detail']?>\n"
-"\n"
-
-#: templates/emails/questionnaire-eha-4weeks
-msgid ""
-"Subject: Questionnaire about your empty property report\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"Four weeks ago, you reported an empty home on ReportEmptyHomes.com with the\n"
-"details provided at the end of this email. To keep our site up to date and\n"
-"relevant, I'd be grateful if you could fill in this short questionnaire to "
-"tell\n"
-"us what has happened:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"Please do not reply to this email; there is a public comment\n"
-"box on the questionnaire.\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"Your report was as follows:\n"
-"\n"
-"<?=$values['title']?>\n"
-"\n"
-"Property type: <?=$values['category']?>\n"
-"\n"
-"<?=$values['detail']?>\n"
-"\n"
-msgstr ""
-"Subject: Holiadur ar eich adroddiad am eiddo gwag\n"
-"\n"
-"Helo <?=$values['name']?>,\n"
-"\n"
-"Bedair wythnos yn ôl, fe wnaethoch adrodd am eiddo gwag ar ReportEmptyHomes."
-"com gan roi'r\n"
-"manylion sydd wedi'u cynnwys ar ddiwedd y neges e-bost hon. Er mwyn cadw'n "
-"safle'n gyfredol\n"
-"ac yn berthnasol, byddwn yn ddiolchgar petaech yn gallu llenwi'r holiadur "
-"byr hwn \n"
-"i ddweud wrthom beth sydd wedi digwydd:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"Peidiwch ag ateb y neges e-bost hon; mae blwch sylwadau cyhoeddus\n"
-"ar yr holiadur.\n"
-"\n"
-"Yn gywir, \n"
-"reportemptyhomes.com\n"
-"\n"
-"Roedd eich adroddiad fel a ganlyn:\n"
-"\n"
-"<?=$values['title']?>\n"
-"\n"
-"Math o eiddo: <?=$values['category']?>\n"
-"\n"
-"<?=$values['detail']?>\n"
-"\n"
-
-#: templates/emails/reply-autoresponse
-msgid ""
-"Subject: Automatic reply to your message to reportemptyhomes.com\n"
-"\n"
-"Hi,\n"
-"\n"
-"This is an automatic response to your email; your email has not been "
-"delivered.\n"
-"\n"
-"If you're replying to an email about a report update, please visit\n"
-"the URL given in the email in order to leave a reply. You cannot\n"
-"reply to an update via email.\n"
-"\n"
-"If you are trying to confirm something, such as a report or an email\n"
-"alert, please click the link in the email that we sent you, or, if\n"
-"you cannot click the link, copy and paste it into the address bar of\n"
-"your web browser.\n"
-"\n"
-"If you're trying to unsubscribe from an email alert, there is an\n"
-"unsubscribe link at the bottom of the email.\n"
-"\n"
-"If you have a question or comment about the site, please send your\n"
-"email to team@fixmystreet.com\n"
-"\n"
-"Yours,\n"
-"The reportemptyhomes.com team\n"
-msgstr ""
-
-#: templates/emails/submit-brent
-msgid ""
-"Subject: FMS Empty property Report: <?=$values['title']?>\n"
-"\n"
-"Dear <?=$values['councils_name']?>,\n"
-"\n"
-"<?=$values['missing']?><?=$values['multiple']?>A user of\n"
-"reportemptyhomes.com has submitted the following report\n"
-"of a local empty property that they believe might require your attention.\n"
-"\n"
-"<?=$values['fuzzy']?>, or to provide an update on the empty property,\n"
-"please visit the following link:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"<?=$values['has_photo']?>----------\n"
-"\n"
-"Name: <?=$values['name']?>\n"
-"\n"
-"Email: <?=$values['email']?>\n"
-"\n"
-"<?=$values['phone_line']?><?=$values['category_line']?>Subject: <?=$values"
-"['title']?>\n"
-"\n"
-"Details: <?=$values['detail']?>\n"
-"\n"
-"<?=$values['easting_northing']?>Latitude: <?=$values['latitude']?>\n"
-"\n"
-"Longitude: <?=$values['longitude']?>\n"
-"\n"
-"<?=$values['closest_address_machine']?>----------\n"
-"\n"
-"Replies to this email will go to the user who submitted the empty property.\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-"\n"
-"[ This message was sent via reportemptyhomes.com, a project of UKCOD, "
-"registered charity\n"
-"number 1076346. If there is a more appropriate email address for messages "
-"about\n"
-"<?=$values['category_footer']?>, please let us know by visiting <http://www."
-"fixmystreet.com/contact>.\n"
-"This will help improve the service for local people. We\n"
-"also welcome any other feedback you may have. ]\n"
-"\n"
-msgstr ""
-
-#: templates/emails/submit-council
-msgid ""
-"Subject: Empty property Report: <?=$values['title']?>\n"
-"\n"
-"Dear <?=$values['councils_name']?>,\n"
-"\n"
-"<?=$values['missing']?><?=$values['multiple']?>A user of\n"
-"reportemptyhomes.com has submitted the following report\n"
-"of a local empty property that they believe might require your attention.\n"
-"\n"
-"<?=$values['fuzzy']?>, or to provide an update on the empty property,\n"
-"please visit the following link:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"<?=$values['has_photo']?>----------\n"
-"\n"
-"Name: <?=$values['name']?>\n"
-"\n"
-"Email: <?=$values['email']?>\n"
-"\n"
-"<?=$values['phone_line']?><?=$values['category_line']?>Subject: <?=$values"
-"['title']?>\n"
-"\n"
-"Details: <?=$values['detail']?>\n"
-"\n"
-"<?=$values['easting_northing']?>Latitude: <?=$values['latitude']?>\n"
-"\n"
-"Longitude: <?=$values['longitude']?>\n"
-"\n"
-"<?=$values['closest_address']?>----------\n"
-"\n"
-"Replies to this email will go to the user who submitted the empty property.\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-"\n"
-"[ This message was sent via reportemptyhomes.com, a project of UKCOD, "
-"registered charity\n"
-"number 1076346. If there is a more appropriate email address for messages "
-"about\n"
-"<?=$values['category_footer']?>, please let us know by visiting <http://www."
-"fixmystreet.com/contact>.\n"
-"This will help improve the service for local people. We\n"
-"also welcome any other feedback you may have. ]\n"
-"\n"
-msgstr ""
-
-#: templates/emails/submit-eha
-msgid ""
-"Subject: Empty property report\n"
-"\n"
-"Dear Empty Property Officer,\n"
-"\n"
-"This is a new referral of an empty property in your area made by a user of "
-"the\n"
-"website ReportEmptyHomes.com; the website user has been told that the case "
-"has\n"
-"been referred to you. We would be grateful if you could do whatever you can "
-"to\n"
-"help get this property back into use. We will contact the user in a month "
-"and\n"
-"again in six months and ask them what has happened to the property. \n"
-"\n"
-"We'd encourage you to tell us what you have done, and when the property "
-"comes\n"
-"back into use, by filling in an update against the property referral on the\n"
-"website:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"This gives useful feedback to website users and helps them understand what\n"
-"action you are taking. We will offer advice to the user on other action "
-"they\n"
-"might take if the property isn't successfully dealt with. \n"
-"\n"
-"<?=$values['has_photo']?>If you would like help or advice on getting empty "
-"properties back into use\n"
-"there is lots of useful information on the Empty Homes Agency's website\n"
-"www.EmptyHomes.com - if you have further questions please give us a call. \n"
-"\n"
-"----------\n"
-"\n"
-"Name: <?=$values['name']?>\n"
-"\n"
-"Email: <?=$values['email']?>\n"
-"\n"
-"<?=$values['phone_line']?>Subject: <?=$values['title']?>\n"
-"\n"
-"Property type: <?=$values['category']?>\n"
-"\n"
-"Details: <?=$values['detail']?>\n"
-"\n"
-"<?=$values['closest_address']?>----------\n"
-"\n"
-"Replies to this email will go to the user who submitted the report, if\n"
-"you would like to ask for any further information.\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-msgstr ""
-"Subject: Adroddiad am eiddo gwag\n"
-"\n"
-"Annwyl Swyddog Eiddo Gwag,\n"
-"\n"
-"Dyma gyfeiriad newydd am eiddo gwag yn eich ardal, a wnaed gan ddefnyddiwr \n"
-"gwefan ReportEmptyHomes.com; dywedwyd wrth ddefnyddiwr y wefan fod yr achos\n"
-"wedi cael ei gyfeirio atoch. Byddem yn ddiolchgar petaech yn gwneud yr hyn "
-"y gallwch i \n"
-"helpu adfer yr eiddo hwn i'w ddefnyddio eto. Byddwn yn cysylltu â'r "
-"defnyddiwr ymhen mis\n"
-"ac eto ymhen chwe mis ac yn gofyn iddynt beth sydd wedi digwydd i'r "
-"eiddo. \n"
-"\n"
-"Hoffwn eich annog chi i ddweud wrthom beth rydych chi wedi'i wneud, a phan y "
-"daw'r eiddo\n"
-"yn ôl mewn defnydd, trwy lenwi diweddariad wrth y cyfeiriad at yr eiddo\n"
-"ar y wefan:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"Mae hyn yn rhoi adborth defnyddiol i ddefnyddwyr y wefan ac yn helpu iddynt "
-"ddeall\n"
-"pa gamau yr ydych yn eu cymryd. Byddwn yn cynnig cyngor i'r defnyddiwr ar "
-"gamau eraill y \n"
-"gall eu cymryd os nad eir i'r afael â'r eiddo'n llwyddiannus. \n"
-"\n"
-"<?=$values['has_photo']?>Os hoffech gael help neu gyngor ar gael eiddo gwag "
-"yn ôl mewn defnydd\n"
-"mae llawer o wybodaeth ddefnyddiol ar wefan yr Asiantaeth Tai Gwag \n"
-"www.EmptyHomes.com - os oes gennych unrhyw gwestiynau eraill, rhowch alwad i "
-"ni. \n"
-"\n"
-"----------\n"
-"\n"
-"Enw: <?=$values['name']?>\n"
-"\n"
-"E-bost: <?=$values['email']?>\n"
-"\n"
-"<?=$values['phone_line']?>Pwnc: <?=$values['title']?>\n"
-"\n"
-"Math o eiddo: <?=$values['category']?>\n"
-"\n"
-"Manylion: <?=$values['detail']?>\n"
-"\n"
-"<?=$values['closest_address']?>----------\n"
-"\n"
-"Bydd ymatebion i'r neges e-bost hon yn mynd at y defnyddiwr a gyflwynodd yr "
-"adroddiad,\n"
-"os hoffech ofyn am fwy o wybodaeth.\n"
-"\n"
-"Yn gywir, \n"
-"reportemptyhomes.com\n"
-"\n"
-
-#: templates/emails/tms-confirm
-msgid ""
-"Subject: Confirm your expression of interest in TextMyStreet\n"
-"\n"
-"Hi,\n"
-"\n"
-"Please click on the link below to confirm your expression of\n"
-"interest in TextMyStreet:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"If you can't click the link, please copy and paste it to the\n"
-"address bar of your web browser.\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-msgstr ""
-
-#: templates/emails/update-confirm
-msgid ""
-"Subject: Confirm your update on reportemptyhomes.com\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"Please click on the link below to confirm the update you just wrote:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"If you can't click the link, please copy and paste it to the\n"
-"address bar of your web browser.\n"
-"\n"
-"Your update reads:\n"
-"\n"
-"<?=$values['update']?>\n"
-"\n"
-"Yours, \n"
-"The reportemptyhomes.com team\n"
-msgstr ""
-"Subject: Cadarnhewch eich diweddariad ar reportemptyhomes.com\n"
-"\n"
-"Helo <?=$values['name']?>,\n"
-"\n"
-"Cliciwch ar y ddolen isod i gadarnhau'r diweddariad yr ydych newydd ei "
-"ysgrifennu:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"Os nad ydych yn gallu clicio ar y ddolen, dylech ei chopïo a'i gludo\n"
-"ym mar cyfeiriad eich porwr gwe.\n"
-"\n"
-"Mae eich diweddariad yn darllen fel a ganlyn:\n"
-"\n"
-"<?=$values['update']?>\n"
-"\n"
-"Yn gywir, \n"
-"Tîm yr Asiantaeth Tai Gwag\n"
-
-#: templates/emails/emptyhomes/alert-problem
-msgid ""
-"Subject: New empty property reports on reportemptyhomes.com\n"
-"\n"
-"The following empty properties have been added:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are new properties,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-"Subject: Adroddiadau newydd am eiddo gwag ar reportemptyhomes.com\n"
-"\n"
-"Mae'r eiddo gwag canlynol wedi cael eu hychwanegu:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yn gywir, \n"
-"reportemptyhomes.com\n"
-"\n"
-"I roi'r gorau i dderbyn negeseuon e-bost pan fydd eiddo newydd yn "
-"ymddangos,\n"
-"dilynwch y ddolen hon: <?=$values['unsubscribe_url']?>\n"
-
-#: templates/emails/emptyhomes/alert-problem-area
-msgid ""
-"Subject: New empty property reports in <?=$values['area_name']?> on "
-"reportemptyhomes.com\n"
-"\n"
-"The following empty properties have been added within\n"
-"<?=$values['area_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are new properties in\n"
-"<?=$values['area_name']?>, please follow this link: \n"
-"<?=$values['unsubscribe_url']?>\n"
-msgstr ""
-"Subject: Adroddiadau newydd am eiddo gwag yn ardal <?=$values['area_name']?> "
-"ar reportemptyhomes.com\n"
-"\n"
-"Mae'r eiddo gwag canlynol wedi cael eu hychwanegu yn ardal\n"
-"<?=$values['area_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yn gywir, \n"
-"reportemptyhomes.com\n"
-"\n"
-"I roi'r gorau i dderbyn negeseuon e-bost pan fydd eiddo newydd yn ymddangos "
-"yn ardal\n"
-"<?=$values['area_name']?>, dilynwch y ddolen hon: \n"
-"<?=$values['unsubscribe_url']?>\n"
-
-#: templates/emails/emptyhomes/alert-problem-council
-msgid ""
-"Subject: New empty property reports reported to <?=$values['area_name']?> on "
-"reportemptyhomes.com\n"
-"\n"
-"The following empty properties have been reported to <?=$values['area_name']?"
-">:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are new properties reported to\n"
-"<?=$values['area_name']?>, please follow this link: \n"
-"<?=$values['unsubscribe_url']?>\n"
-msgstr ""
-"Subject: Adroddiadau newydd am eiddo gwag a hysbyswyd i ardal <?=$values"
-"['area_name']?> ar reportemptyhomes.com\n"
-"\n"
-"Mae'r eiddo gwag canlynol wedi cael eu hysbysu i ardal<?=$values"
-"['area_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yn gywir, \n"
-"reportemptyhomes.com\n"
-"\n"
-"I roi'r gorau i dderbyn negeseuon e-bost pan gaiff eiddo gwag newydd eu "
-"hysbysu i ardal \n"
-"<?=$values['area_name']?>, dilynwch y ddolen hon: \n"
-"<?=$values['unsubscribe_url']?>\n"
-
-#: templates/emails/emptyhomes/alert-problem-nearby
-msgid ""
-"Subject: New nearby empty properties on reportemptyhomes.com\n"
-"\n"
-"The following nearby empty properties have been added:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are nearby properties,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-"Subject: Eiddo gwag cyfagos newydd ar reportemptyhomes.com\n"
-"\n"
-"Mae'r eiddo gwag cyfagos newydd wedi cael eu hychwanegu:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yn gywir, \n"
-"reportemptyhomes.com\n"
-"\n"
-"I roi'r gorau i dderbyn negeseuon e-bost pan fydd eiddo gerllaw,\n"
-"dilynwch y ddolen hon: <?=$values['unsubscribe_url']?>\n"
-
-#: templates/emails/emptyhomes/alert-problem-ward
-msgid ""
-"Subject: New empty property reports reported to <?=$values['area_name']?> "
-"within <?=$values['ward_name']?> on reportemptyhomes.com\n"
-"\n"
-"The following empty properties have been reported to <?=$values['area_name']?"
-">\n"
-"within <?=$values['ward_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are new properties reported to\n"
-"<?=$values['area_name']?> within <?=$values['ward_name']?>,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-"Subject: Adroddiadau newydd am eiddo gwag a hysbyswyd i ardal <?=$values"
-"['area_name']?>, yn ward <?=$values['ward_name']?> ar reportemptyhomes.com\n"
-"\n"
-"Mae'r eiddo gwag canlynol wedi cael eu hysbysu i ardal <?=$values"
-"['area_name']?>,\n"
-"o fewn ward <?=$values['ward_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yn gywir, \n"
-"reportemptyhomes.com\n"
-"\n"
-"I roi'r gorau i dderbyn negeseuon e-bost pan gaiff eiddo newydd eu hysbysu i "
-"ardal \n"
-"<?=$values['area_name']?>, o fewn ward <?=$values['ward_name']?>,\n"
-"dilynwch y ddolen hon: <?=$values['unsubscribe_url']?>\n"
-
-#: templates/emails/emptyhomes/alert-update
-msgid ""
-"Subject: New updates on empty property- '<?=$values['title']?>'\n"
-"\n"
-"The following updates have been left on this empty property:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"View or reply to these updates: <?=$values['problem_url']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are new updates on this property,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-"Subject: Diweddariadau newydd am eiddo gwag - '<?=$values['title']?>'\n"
-"\n"
-"Mae'r diweddariadau canlynol wedi cael eu gadael am yr eiddo gwag hwn:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Gweld y diweddariadau hyn neu eu hateb: <?=$values['problem_url']?>\n"
-"\n"
-"Yn gywir, \n"
-"reportemptyhomes.com\n"
-"\n"
-"I roi'r gorau i dderbyn negeseuon e-bost pan fydd diweddariadau newydd yn "
-"ymddangos am yr eiddo hwn,\n"
-"dilynwch y ddolen hon: <?=$values['unsubscribe_url']?>\n"
+#~ msgid "You have successfully subscribed to that alert."
+#~ msgstr "Rydych wedi tanysgrifio'n llwyddiannus i'r hysbysiad hwnnw."
+
+#~ msgid "We could not validate that alert."
+#~ msgstr "Ni allem ddilysu'r hysbysiad hwnnw."
+
+#~ msgid "Please give your name"
+#~ msgstr "Rhowch eich enw"
+
+#~ msgid "Please give a subject"
+#~ msgstr "Rhowch destun"
+
+#~ msgid "Weird and Wonderful reports"
+#~ msgstr "Adroddiadau rhyfeddol a rhagorol"
+
+#~ msgid ""
+#~ "You have successfully confirmed your report and you can now <a href=\"%s"
+#~ "\">view it on the site</a>."
+#~ msgstr ""
+#~ "Rydych wedi cadarnhau eich adroddiad yn llwyddiannus ac nawr gallwch <a "
+#~ "href=\"%s\">weld eich adroddiad ar y safle</a>."
+
+#~ msgid "Receive email when updates are left on this empty property"
+#~ msgstr ""
+#~ "Hoffwn dderbyn neges e-bost pan gaiff diweddariadau eu rhoi am yr eiddo "
+#~ "gwag hwn"
+
+#, fuzzy
+#~ msgid "New empty property reports NEAR {{POSTCODE}} on reportemptyhomes.com"
+#~ msgstr "Adroddiadau newydd i gyngor {{COUNCIL}} ar reportemptyhomes.com"
+
+#, fuzzy
+#~ msgid "New empty property reports within {{NAME}}"
+#~ msgstr "Adroddiadau newydd am eiddo gwag"
+
+#, fuzzy
+#~ msgid "The latest empty properties within {{NAME}}"
+#~ msgstr "Yr eiddo gwag diweddaraf y mae defnyddwyr wedi rhoi gwybod amdanynt"
+
+#, fuzzy
+#~ msgid "View update on site"
+#~ msgstr "Diweddariadau am {{title}}"
#~ msgid "Thank you for reporting an empty property on ReportEmptyHomes.com."
#~ msgstr "Diolch am roi gwybod am eiddo gwag ar ReportEmptyHomes.com."
@@ -3607,21 +3045,12 @@ msgstr ""
#~ msgid "empty property"
#~ msgstr "eiddo gwag"
-#~ msgid "Empty Homes Agency"
-#~ msgstr "Yr Asiantaeth Tai Gwag"
-
#~ msgid "Local alerts"
#~ msgstr "Rhybuddion lleol"
#~ msgid "Help"
#~ msgstr "Help"
-#~ msgid "Get Empty Homes Agency on your iPhone"
-#~ msgstr "Cael yr Asiantaeth Tai Gwag ar eich iPhone"
-
-#~ msgid "New!"
-#~ msgstr "Newydd!"
-
#~ msgid ""
#~ "That empty property has been hidden from public view as it contained "
#~ "inappropriate public details"
diff --git a/locale/nb_NO.UTF-8/LC_MESSAGES/FixMyStreet.po b/locale/nb_NO.UTF-8/LC_MESSAGES/FixMyStreet.po
index 9637ef741..bdc00168b 100644
--- a/locale/nb_NO.UTF-8/LC_MESSAGES/FixMyStreet.po
+++ b/locale/nb_NO.UTF-8/LC_MESSAGES/FixMyStreet.po
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: FixMyStreet\n"
"Report-Msgid-Bugs-To: matthew@mysociety.org\n"
-"POT-Creation-Date: 2011-05-04 19:21+0100\n"
+"POT-Creation-Date: 2011-06-09 18:49+0100\n"
"PO-Revision-Date: 2011-04-07 09:00MET\n"
"Last-Translator: Petter Reinholdtsen <pere@hungry.com>\n"
"Language-Team: Norwegian Bokmål <i18n-nb@lister.ping.uio.no>\n"
@@ -19,631 +19,679 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: KBabel 1.11.4\n"
-#: perllib/Cobrands/Emptyhomes/Util.pm:61 bin/send-questionnaires-eha:93
-msgid "Report Empty Homes"
-msgstr "Rapporter tomme hjem"
-
-#: perllib/Cobrands/Fiksgatami/Util.pm:37
-msgid "Enter a nearby postcode, or street name and area"
-msgstr "Skriv inn postnummer i nærheten, eller veinavn og sted"
-
-#: perllib/Page.pm:86
-#, perl-format
-msgid ""
-"Please try again later, or <a href=\"mailto:%s\">email us</a> to let us know."
-msgstr ""
-"Vær så snill å forsøk igjen senere, eller <a href=\"mailto:%s\">send oss en "
-"epost</a> og gi oss beskjed."
-
-#: perllib/Page.pm:87
-msgid "Sorry! Something's gone wrong."
-msgstr "Beklager! Noe har gått galt."
-
-#: perllib/Page.pm:88
-msgid "The text of the error was:"
-msgstr "Teksten i feilmeldingen var:"
-
-#: perllib/Page.pm:197 web/contact.cgi:107
-msgid "FixMyStreet"
-msgstr "FiksGataMi"
-
-#: perllib/Page.pm:200 perllib/Page.pm:324
-msgid "Report a problem"
-msgstr "Rapporter et problem"
-
-#: perllib/Page.pm:201 perllib/Page.pm:325
-msgid "All reports"
-msgstr "Alle rapporter"
-
-#: perllib/Page.pm:202 perllib/Page.pm:326
-msgid "Local alerts"
-msgstr "Lokale varsler"
-
-#: perllib/Page.pm:203 perllib/Page.pm:327
-msgid "Help"
-msgstr "Hjelp"
-
-#: perllib/Page.pm:204 web/about.cgi:19 web/about.cgi:21
-msgid "About us"
-msgstr "Om oss"
-
-#: perllib/Page.pm:225
-msgid "Fix<span id=\"my\">My</span>Street"
-msgstr "Fiks<span id=\"my\">Gata</span>Mi"
-
-#: perllib/Page.pm:303
-msgid ""
-"This is a developer site; things might break at any time, and the database "
-"will be periodically deleted."
-msgstr ""
-"Dette er et utviklernettsted. Ting kan knekke når som helst og databasen vil "
-"bli periodisk slettet."
-
-#: perllib/Page.pm:317
-msgid ""
-"Built by <a href=\"http://www.mysociety.org/\">mySociety</a>, using some <a "
-"href=\"http://github.com/mysociety/fixmystreet\">clever</a>&nbsp;<a href="
-"\"https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa"
-"\">code</a>."
-msgstr ""
-"Bygget av <a href=\"http://www.mysociety.org/\">mySociety</a>, ved hjelp av "
-"<a href=\"http://github.com/mysociety/fixmystreet\">smart</a>&nbsp;<a href="
-"\"https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa"
-"\">kode</a>."
-
-#: perllib/Page.pm:319
-msgid ""
-"Built by <a href=\"http://www.mysociety.org/\">mySociety</a> and maintained "
-"by <a href=\"http://www.nuug.no/\">NUUG</a>, using some <a href=\"http://"
-"github.com/mysociety/fixmystreet\">clever</a>&nbsp;<a href=\"https://secure."
-"mysociety.org/cvstrac/dir?d=mysociety/services/TilMa\">code</a>."
-msgstr ""
-"Bygget av <a href=\"http://www.mysociety.org/\">mySociety</a> og "
-"vedlikeholdt av <a href=\"http://www.nuug.no/\">NUUG</a>, ved hjelp av <a "
-"href=\"http://github.com/mysociety/fixmystreet\">snedig</a>&nbsp;<a href="
-"\"https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa"
-"\">kode</a>."
-
-#: perllib/Page.pm:323
-msgid "Navigation"
-msgstr "Navigasjon"
-
-#: perllib/Page.pm:328
-msgid "Contact"
-msgstr "Kontakt"
-
-#: perllib/Page.pm:330
-msgid ""
-"<a href=\"http://www.mysociety.org/\"><img id=\"logo\" width=\"133\" height="
-"\"26\" src=\"/i/mysociety-dark.png\" alt=\"View mySociety.org\"><span id="
-"\"logoie\"></span></a>"
-msgstr ""
-"<div id=\"logo\" align=\"center\"><a href=\"http://www.nuug.no/\">Foreningen "
-"NUUG</a></div>"
-
-#: perllib/Page.pm:388
-msgid "Error"
-msgstr "Feil"
-
-#: perllib/Page.pm:461
-msgid "your problem will not be posted"
-msgstr "vil ditt problem ikke bli publisert"
-
-#: perllib/Page.pm:462
-msgid "we'll hang on to your problem report while you're checking your email."
-msgstr "vi holder på rapporten din mens du sjekker epost."
-
-#: perllib/Page.pm:464
-msgid "your update will not be posted"
-msgstr "din oppdatering vil ikke bli publisert"
-
-#: perllib/Page.pm:465
-msgid "we'll hang on to your update while you're checking your email."
-msgstr "vi holder på din oppdatering mens du sjekker din epost."
-
-#: perllib/Page.pm:467
-msgid "your alert will not be activated"
-msgstr "vil ditt varsel ikke bli aktivisert"
+#: bin/send-reports:173
+msgid " and "
+msgstr " og "
-#: perllib/Page.pm:468
-msgid "we'll hang on to your alert while you're checking your email."
-msgstr "vi holder på ditt varsel mens du sjekker din epost."
+#: templates/web/default/tokens/confirm_problem.html:10
+#: templates/web/default/tokens/confirm_problem.html:6
+msgid " and <strong>we will now send it to the council</strong>"
+msgstr " og <strong>vi sender det nå til administrasjonen</strong>"
-#: perllib/Page.pm:475
-#, perl-format
-msgid ""
-"<h1>Nearly Done! Now check your email...</h1>\n"
-"<p>The confirmation email <strong>may</strong> take a few minutes to arrive "
-"&mdash; <em>please</em> be patient.</p>\n"
-"<p>If you use web-based email or have 'junk mail' filters, you may wish to "
-"check your bulk/spam mail folders: sometimes, our messages are marked that "
-"way.</p>\n"
-"<p>You must now click the link in the email we've just sent you &mdash;\n"
-"if you do not, %s.</p>\n"
-"<p>(Don't worry &mdash; %s)</p>\n"
-msgstr ""
-"<h1>Nesten ferdig! Nå må du sjekke eposten din...</h1>\n"
-"<p>Bekreftelseseposten <strong>kan</strong> bruke noen minutter på å nå frem "
-"&mdash; så vær tålmodig.</p>\n"
-"<p>Hvis du bruker webbasert epost eller har søppelepost-filter, så bør du "
-"kanskje a sjekke din bulk/spam-mappe. Noen ganger blir epost fra oss markert "
-"som søppelpost.</p>\n"
-"<p>Du må klikke på lenken i eposten vi nettopp har sendt deg &mdash;\n"
-"hvis du ikke gjør det, %s.</p>\n"
-"<p>(Ingen grunn til bekymring &mdash; %s)</p>\n"
-
-#: perllib/Page.pm:503
-msgid "today"
-msgstr "idag"
+#: templates/web/default/report/new/all_councils_text.html:10
+#: templates/web/default/report/new/all_councils_text.html:3
+#: templates/web/default/report/new/no_councils_text.html:15
+#: templates/web/default/report/new/no_councils_text.html:3
+#: templates/web/default/report/new/some_councils_text.html:20
+#: templates/web/default/report/new/some_councils_text.html:23
+#: templates/web/default/report/new/some_councils_text.html:5
+#: templates/web/emptyhomes/report/new/all_councils_text.html:2
+msgid " or "
+msgstr " eller "
-#: perllib/Page.pm:527
-msgid "less than a minute"
-msgstr "mindre enn et minutt"
+#: templates/web/default/admin/council_list.html:17
+msgid "%d addresses"
+msgstr "%d adresser"
-#: perllib/Page.pm:530
-#, perl-format
-msgid "%d week"
-msgstr "%d uke"
+#: templates/web/default/admin/index.html:16
+msgid "%d confirmed alerts, %d unconfirmed"
+msgstr "%d bekreftede varsler, %d ubekreftede"
-#: perllib/Page.pm:530
-#, perl-format
-msgid "%d weeks"
-msgstr "%d uker"
+#: templates/web/default/admin/index.html:18
+msgid "%d council contacts &ndash; %d confirmed, %d unconfirmed"
+msgstr "%d administrasjonskontakter &ndash; %d bekreftet, %d ubekreftet"
-#: perllib/Page.pm:531
-#, perl-format
+#: perllib/Utils.pm:245
msgid "%d day"
msgstr "%d dag"
-#: perllib/Page.pm:531
-#, perl-format
+#: perllib/Utils.pm:245
msgid "%d days"
msgstr "%d dager"
-#: perllib/Page.pm:532
-#, perl-format
+#: templates/web/default/admin/council_list.html:27
+msgid "%d edits by %s"
+msgstr "%d redigeringer av %s"
+
+#: perllib/Utils.pm:246
msgid "%d hour"
msgstr "%d time"
-#: perllib/Page.pm:532
-#, perl-format
+#: perllib/Utils.pm:246
msgid "%d hours"
msgstr "%d timer"
-#: perllib/Page.pm:533
-#, perl-format
+#: templates/web/default/admin/index.html:15
+msgid "%d live updates"
+msgstr "%d aktive oppdateringer"
+
+#: perllib/Utils.pm:247
msgid "%d minute"
msgstr "%d minutt"
-#: perllib/Page.pm:533
-#, perl-format
+#: perllib/Utils.pm:247
msgid "%d minutes"
msgstr "%d minutter"
-#: perllib/Page.pm:553
-#, perl-format
+#: templates/web/default/admin/index.html:17
+#, fuzzy
+msgid "%d questionnaires sent &ndash; %d answered (%s%%)"
+msgstr "%d spørreskjema sendt &ndash; %d besvart (%d%%)"
+
+#: perllib/Utils.pm:244
+msgid "%d week"
+msgstr "%d uke"
+
+#: perllib/Utils.pm:244
+msgid "%d weeks"
+msgstr "%d uker"
+
+#: templates/web/default/reports/council.html:11
+#: templates/web/default/reports/council.html:12
+msgid "%s - Summary reports"
+msgstr "%s - oppsummeringsrapporter"
+
+#: perllib/FixMyStreet/Cobrand/Default.pm:738
+#: perllib/FixMyStreet/Cobrand/Default.pm:752
+msgid "%s ward, %s"
+msgstr ""
+
+#: perllib/FixMyStreet/DB/Result/Problem.pm:307
msgid "%s, reported anonymously at %s"
msgstr "%s, rapportert anonymt %s"
-#: perllib/Page.pm:555
-#, perl-format
+#: perllib/FixMyStreet/DB/Result/Problem.pm:309
msgid "%s, reported by %s at %s"
msgstr "%s, rapportert av %s %s"
-#: perllib/Page.pm:558 perllib/Page.pm:560 perllib/Page.pm:566
-#: perllib/Page.pm:568 web/index.cgi:408 web/index.cgi:594 web/index.cgi:603
-#: bin/send-reports:186 bin/send-reports:195
-msgid "Other"
-msgstr "Annet"
+#: perllib/FixMyStreet/Cobrand/Default.pm:769
+#: perllib/FixMyStreet/Cobrand/Default.pm:783
+#, fuzzy
+msgid "%s, within %s ward"
+msgstr "Problemer innenfor %s avdeling/etat"
-#: perllib/Page.pm:559
-#, perl-format
-msgid "Reported by %s in the %s category anonymously at %s"
-msgstr "Rapportert av %s i kategorien %s anonnymt %s"
+#: perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm:21
+#: perllib/FixMyStreet/Map/Tilma/Original/1_10k.pm:21
+msgid ""
+"&copy; Crown copyright. All rights reserved. Ministry of Justice "
+"100037819&nbsp;2008."
+msgstr ""
+"&copy; Crown copyright. Alle rettigheter reservert. Justisdepartementet "
+"100037819&nbsp;2008."
-#: perllib/Page.pm:561
-#, perl-format
-msgid "Reported by %s in the %s category by %s at %s"
-msgstr "Rapportert av %s i kategorien %s av %s %s"
+#: templates/web/default/email_sent.html:32
+msgid "(Don't worry &mdash; %s)"
+msgstr ""
-#: perllib/Page.pm:563
-#, perl-format
-msgid "Reported by %s anonymously at %s"
-msgstr "Publisert av %s anonymt %s"
+#: templates/web/default/alert/list.html:53
+msgid "(a default distance which covers roughly 200,000 people)"
+msgstr "(en default-avstand som dekker en befolkning på omtrent 200 000)"
-#: perllib/Page.pm:565
-#, perl-format
-msgid "Reported by %s by %s at %s"
-msgstr "Rapporter av %s av %s %s"
+#: templates/web/default/alert/list.html:58
+msgid "(alternatively the RSS feed can be customised, within"
+msgstr "(alternativt kan RSS-strømmen tilpasses, innenfor"
-#: perllib/Page.pm:567
-#, perl-format
-msgid "Reported in the %s category anonymously at %s"
-msgstr "Rapportert i kategorien %s anonymt %s"
+#: templates/web/default/around/around_map_list_items.html:10
+#: templates/web/default/around/on_map_list_items.html:7
+msgid "(fixed)"
+msgstr "(løst)"
-#: perllib/Page.pm:569
-#, perl-format
-msgid "Reported in the %s category by %s at %s"
-msgstr "Rapportert i kategorien %s av %s %s"
+#: templates/web/default/index.html:4 templates/web/default/index.html:8
+msgid "(like graffiti, fly tipping, broken paving slabs, or street lighting)"
+msgstr "(som tagging, søppel, hull i veien, eller manglende gatelys)"
-#: perllib/Page.pm:571 web/contact.cgi:212
-#, perl-format
-msgid "Reported anonymously at %s"
-msgstr "Rapportert anonymt %s"
+#: templates/web/default/reports/council.html:90
+msgid "(not sent to council)"
+msgstr "(ikke rapportert til administrasjonen)"
-#: perllib/Page.pm:573 web/contact.cgi:213
-#, perl-format
-msgid "Reported by %s at %s"
-msgstr "Publisert av %s %s"
+#: templates/web/default/report/display.html:82
+#: templates/web/default/report/new/fill_in_details.html:160
+msgid "(optional)"
+msgstr "(valgfritt)"
-#: perllib/Page.pm:578
-msgid "the map was not used so pin location may be inaccurate"
-msgstr "kartet ble ikke brukt, så nåleposisjon kan være unøyaktig"
+#: templates/web/default/reports/council.html:88
+msgid "(sent to both)"
+msgstr "(sent til begge)"
-#: perllib/Page.pm:591
-#, perl-format
-msgid "Sent to %s %s later"
-msgstr "Sendt til %s %s senere"
+#: templates/web/default/report/display.html:88
+#: templates/web/default/report/new/fill_in_details.html:145
+msgid "(we never show your email address or phone number)"
+msgstr "(vi viser aldri din epostadresse eller telefonnummer)"
-#: perllib/Page.pm:595
-msgid "Not reported to council"
-msgstr "Ikke rapportert til administrasjonen"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:261
+msgid "*unknown*"
+msgstr "*ukjent*"
-#: perllib/Page.pm:646 web-admin/index.cgi:523 web-admin/index.cgi:647
-msgid "Updates"
-msgstr "Oppdateringer"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:475
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:501
+#: perllib/FixMyStreet/DB/Result/Problem.pm:204
+msgid "-- Pick a category --"
+msgstr "-- velg en kategori --"
-#: perllib/Page.pm:650
-#, perl-format
-msgid "Posted by %s at %s"
-msgstr "Sendt inn av %s %s"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:461
+#: perllib/FixMyStreet/DB/Result/Problem.pm:210
+msgid "-- Pick a property type --"
+msgstr "-- velg en eiendomsstype --"
-#: perllib/Page.pm:652
-#, perl-format
-msgid "Posted anonymously at %s"
-msgstr "Publisert anonymt %s"
+#: templates/web/default/tokens/confirm_problem.html:14
+#: templates/web/default/tokens/confirm_problem.html:6
+msgid ". You can <a href=\"%s\">view the problem on this site</a>."
+msgstr ". Du kan <a href=\"%s\">lese saken på portalen </a>."
-#: perllib/Page.pm:655
-msgid "marked as fixed"
-msgstr "markert som fikset"
+#: templates/web/default/footer.html:13
+msgid ""
+"<a href=\"http://www.mysociety.org/\"><img id=\"logo\" width=\"133\" height="
+"\"26\" src=\"/i/mysociety-dark.png\" alt=\"View mySociety.org\"><span id="
+"\"logoie\"></span></a>"
+msgstr ""
+"<div id=\"logo\" align=\"center\"><a href=\"http://www.nuug.no/\">Foreningen "
+"NUUG</a></div>"
-#: perllib/Page.pm:656
-msgid "reopened"
-msgstr "åpnet på nytt"
+#: templates/web/default/questionnaire/completed.html:21
+#, fuzzy
+msgid ""
+"<p style=\"font-size:150%\">Thank you very much for filling in our "
+"questionnaire; glad to hear it&rsquo;s been fixed.</p>"
+msgstr ""
+"<p style=\"font-size:150%\">Tusen takk for at du fylte ut vårt "
+"spørreskjema.\n"
+"Veldig glad for å høre at det er blitt fikset.</p>\n"
-#: perllib/Page.pm:664 web/index.cgi:1106
-msgid "Offensive? Unsuitable? Tell us"
-msgstr "Støtende? Upassende? Gi oss beskjed"
+#: templates/web/default/questionnaire/completed.html:11
+#, fuzzy
+msgid ""
+"<p style=\"font-size:150%%\">We&rsquo;re sorry to hear that. We have two "
+"suggestions: why not try\n"
+"<a href=\"%s\">writing direct to your councillor(s)</a>\n"
+"or, if it&rsquo;s a problem that could be fixed by local people working "
+"together,\n"
+"why not <a href=\"http://www.pledgebank.com/new\">make and publicise a "
+"pledge</a>?\n"
+"</p>"
+msgstr ""
+"<p style=\"font-size:150%%\">Det gjør oss triste å høre dette. Vi har to "
+"forslag: hva med å forsøke\n"
+"<a href=\"%s\">Ã¥ skrive direkte til dine representanter</a>, eller hvis det "
+"er et problem som kan fikses\n"
+"av naboer som jobber sammen, hva med å <a href=\"http://www.pledgebank.com/"
+"new\">publisere en utfordring om å bidra</a>?</p>\n"
-#: perllib/Page.pm:691
-msgid "That postcode was not recognised, sorry."
-msgstr "Det postnummeret ble ikke gjenkjent, beklager."
+#: templates/web/default/questionnaire/index.html:37
+msgid ""
+"<p>Getting empty homes back into use can be difficult, but by now a good "
+"council\n"
+"will have made a lot of progress and reported what they have done on the\n"
+"website. Even so properties can remain empty for many months if the owner "
+"is\n"
+"unwilling or the property is in very poor repair. If nothing has happened "
+"or\n"
+"you are not satisfied with the progress the council is making, now is the "
+"right\n"
+"time to say so. We think it&rsquo;s a good idea to contact some other people "
+"who\n"
+"may be able to help or put pressure on the council For advice on how to do\n"
+"this and other useful information please go to <a\n"
+"href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://www."
+"emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
+msgstr ""
-#: perllib/Page.pm:697
+#: templates/web/default/questionnaire/index.html:28
msgid ""
-"Sorry, that appears to be a Crown dependency postcode, which we don't cover."
+"<p>Getting empty homes back into use can be difficult. You shouldn&rsquo;t "
+"expect\n"
+"the property to be back into use yet. But a good council will have started "
+"work\n"
+"and should have reported what they have done on the website. If you are not\n"
+"satisfied with progress or information from the council, now is the right "
+"time\n"
+"to say. You may also want to try contacting some other people who may be "
+"able\n"
+"to help. For advice on how to do this and other useful information please\n"
+"go to <a href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://"
+"www.emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
msgstr ""
-"Beklager det ser ut til å være et \"Crown dependency\"-postnummer, som vi "
-"ikke dekker."
-#: perllib/Page.pm:700 perllib/FixMyStreet/Geocode.pm:145
+#: templates/web/default/questionnaire/completed.html:5
+#, fuzzy
msgid ""
-"We do not cover Northern Ireland, I'm afraid, as our licence doesn't include "
-"any maps for the region."
+"<p>Thank you very much for filling in our questionnaire; if you\n"
+"get some more information about the status of your problem, please come back "
+"to the\n"
+"site and leave an update.</p>"
msgstr ""
-"Vi dekker ikke nordlige Irland, er jeg redd, da vår lisens ikke inkluderer "
-"noen kart for den regionen."
+"<p>Tusen takk for at du fylte ut vårt spørreskjema. Hvis du\n"
+"får mer informasjon om status for ditt problem, vær så snill å kom tilbake "
+"hit til\n"
+"nettstedet og legg igjen en oppdatering.</p>\n"
-#: perllib/Page.pm:731
-msgid "Please upload a JPEG image only"
-msgstr "Vennligst last kun opp et JPEG-bilde"
+#: templates/web/default/around/display_location.html:93
+#: templates/web/default/around/display_location.html:95
+#, fuzzy
+msgid ""
+"<small>If you cannot see the map, <a href='%s' rel='nofollow'>skip this "
+"step</a>.</small>"
+msgstr ""
+"<small>Hvis du ikke kan se kartet, <a href='%s' rel='nofollow'>hopp over "
+"dette\n"
+" steget</a>.</small>"
-#: perllib/FixMyStreet/Alert.pm:147
-msgid "This report is currently marked as fixed."
-msgstr "Denne rapporten er for tiden markert som fikset."
+#: templates/web/default/admin/index.html:14
+msgid "<strong>%d</strong> live problems"
+msgstr "<strong>%d</strong> aktive problemer"
-#: perllib/FixMyStreet/Alert.pm:148
-msgid "This report is currently marked as open."
-msgstr "Denne rapporten er for tiden markert som åpen."
+#: templates/web/default/static/about.html:1
+#: templates/web/default/static/about.html:3
+#: templates/web/emptyhomes/header.html:34
+#: templates/web/emptyhomes/static/about.html:1
+#: templates/web/emptyhomes/static/about.html:3
+msgid "About us"
+msgstr "Om oss"
-#: perllib/FixMyStreet/Alert.pm:313
-#, perl-format
-msgid "Report on %s"
-msgstr "Rapport på %s"
+#: templates/web/default/admin/council_contacts.html:50
+msgid "Add new category"
+msgstr "Legg til ny kategori"
-#: perllib/FixMyStreet/Geocode.pm:73
-msgid ""
-"Sorry, that location appears to be too general; please be more specific."
+#: templates/web/default/auth/change_password.html:30
+msgid "Again:"
msgstr ""
-"Beklager, den plasserigen ser ut til å være for generell. Forsøk å være mer "
-"spesifikk."
-#: perllib/FixMyStreet/Geocode.pm:83
-msgid "That location does not appear to be in Britain; please try again."
-msgstr "Det stedet virker ikke å være i storbritannia. Vennligst prøv igjen."
+#: templates/web/default/admin/timeline.html:37
+msgid "Alert %d created for %s, type %s, parameters %s / %s"
+msgstr "Varsel %d opprettet for %s, type %s, parameter %s / %s"
-#: perllib/FixMyStreet/Geocode.pm:105 perllib/FixMyStreet/Geocode.pm:140
-msgid "Sorry, we could not find that location."
-msgstr "Beklager, vi kunne ikke finne det stedet."
+#: templates/web/default/admin/timeline.html:39
+msgid "Alert %d disabled (created %s)"
+msgstr "Varsel %d koblet ut (opprettet %s)"
-#: perllib/FixMyStreet/Geocode.pm:138
-msgid "Sorry, we could not parse that location. Please try again."
-msgstr "Beklager, vi kunne ikke tolke den posisjonen. Vennligst prøv på nytt."
+#: templates/web/default/report/display.html:133
+msgid "Alert me to future updates"
+msgstr "Send meg varsel ved fremtidige oppdateringer"
-#: perllib/FixMyStreet/Geocode.pm:158
-msgid ""
-"We found more than one match for that location. We show up to ten matches, "
-"please try a different search if yours is not here."
-msgstr ""
-"Vi fant mer en ett treff for den plassen. Vi viser opp til ti treff, så "
-"forsøk et annet søk hvis din plass ikke er her."
+#: templates/web/default/admin/council_list.html:44
+msgid "All confirmed"
+msgstr "Alle bekreftet"
+
+#: templates/web/default/footer.html:7 templates/web/emptyhomes/header.html:31
+#: templates/web/fiksgatami/footer.html:6
+msgid "All reports"
+msgstr "Alle rapporter"
-#: perllib/FixMyStreet/Geocode.pm:173
-msgid "More than one match"
-msgstr "Mer enn ett treff"
+#: templates/web/default/report/new/some_councils_text.html:2
+msgid "All the information you provide here will be sent to"
+msgstr "All informasjonen du har lagt inn her vil bli sendt til"
-#: perllib/FixMyStreet/Map/Bing.pm:41
-#: perllib/FixMyStreet/Map/OSM/StreetView.pm:42
-#: perllib/FixMyStreet/Map/Google.pm:41
-#: perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm:51
-#: perllib/FixMyStreet/Map/Tilma/Original/StreetView.pm:21
+#: templates/web/default/report/new/all_councils_text.html:3
+#: templates/web/default/report/new/all_councils_text.html:5
+#, fuzzy
msgid ""
-"Map contains Ordnance Survey data &copy; Crown copyright and database right "
-"2010."
+"All the information you provide here will be sent to <strong>%s</strong> or "
+"a relevant local body such as <strong>TfL</strong>, via the London Report-It "
+"system."
msgstr ""
-"Kartet inneholder data fra Ordnance Survey &copy; Crown copyright og "
-"databaserettigheter 2010."
+"All informasjonen du gir oss her vil bli sendt til <strong>%s</strong>.\n"
+"Tittelen og detaljene for problemet vil bli offentlig, pluss ditt navn\n"
+"hvis du gir oss tillatelse."
-#: perllib/FixMyStreet/Map/OSM.pm:76
+#: templates/web/default/report/new/all_councils_text.html:10
+#: templates/web/default/report/new/all_councils_text.html:12
+#, fuzzy
msgid ""
-"Map &copy; <a id=\"osm_link\" href=\"http://www.openstreetmap.org/"
-"\">OpenStreetMap</a> and contributors, <a href=\"http://creativecommons.org/"
-"licenses/by-sa/2.0/\">CC-BY-SA</a>"
+"All the information you provide here will be sent to <strong>%s</strong>."
+msgstr "All informasjonen du har lagt inn her vil bli sendt til"
+
+#: templates/web/emptyhomes/report/new/all_councils_text.html:2
+#: templates/web/emptyhomes/report/new/all_councils_text.html:4
+#, fuzzy
+msgid ""
+"All the information you provide here will be sent to <strong>%s</strong>. On "
+"the site, we will show the subject and details of the problem, plus your "
+"name if you give us permission."
msgstr ""
-"Kart &copy; <a id=\"osm_link\" href=\"http://www.openstreetmap.org/"
-"\">OpenStreetMap</a> og bidragsytere, <a href=\"http://creativecommons.org/"
-"licenses/by-sa/2.0/\">CC-BY-SA</a>"
+"All informasjonen du legger inn her vil bli sendt til <strong>%s</strong>.\n"
+"PÃ¥ dette nettestedet vil vi vise tema og detaljer om problemet, pluss ditt "
+"navn\n"
+"hvis du gir oss lov."
-#: perllib/FixMyStreet/Map/OSM.pm:115
-#: perllib/FixMyStreet/Map/Tilma/Original.pm:155
-msgid "Problem"
-msgstr "Problem"
+#: templates/web/default/questionnaire/index.html:62
+msgid "An update marked this problem as fixed."
+msgstr "En oppdatering markerte dette problemet som fikset."
+
+#: templates/web/default/admin/list_updates.html:10
+#: templates/web/default/admin/search_reports.html:18
+msgid "Anonymous"
+msgstr "Anonym"
-#: perllib/FixMyStreet/Map/BingOL.pm:44
+#: templates/web/default/admin/report_edit.html:17
+#: templates/web/default/admin/update_edit.html:14
+msgid "Anonymous:"
+msgstr "Anonym:"
+
+#: templates/web/fiksgatami/footer.html:15
msgid ""
-"Map contains Ordnance Survey data &copy; Crown copyright and database right "
-"2010. Microsoft"
+"Built by <a href=\"http://www.mysociety.org/\">mySociety</a> and maintained "
+"by <a href=\"http://www.nuug.no/\">NUUG</a>, using some <a href=\"http://"
+"github.com/mysociety/fixmystreet\">clever</a>&nbsp;<a href=\"https://secure."
+"mysociety.org/cvstrac/dir?d=mysociety/services/TilMa\">code</a>."
msgstr ""
-"Kartet inneholder data fra Ordnance Survey &copy; Crown copyright og "
-"databaserettigheter 2010. Microsoft"
+"Bygget av <a href=\"http://www.mysociety.org/\">mySociety</a> og "
+"vedlikeholdt av <a href=\"http://www.nuug.no/\">NUUG</a>, ved hjelp av <a "
+"href=\"http://github.com/mysociety/fixmystreet\">snedig</a>&nbsp;<a href="
+"\"https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa"
+"\">kode</a>."
-#: perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm:51
-#: perllib/FixMyStreet/Map/Tilma/Original/1_10k.pm:21
+#: templates/web/default/footer.html:16
msgid ""
-"&copy; Crown copyright. All rights reserved. Ministry of Justice "
-"100037819&nbsp;2008."
+"Built by <a href=\"http://www.mysociety.org/\">mySociety</a>, using some <a "
+"href=\"http://github.com/mysociety/fixmystreet\">clever</a>&nbsp;<a href="
+"\"https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa"
+"\">code</a>."
msgstr ""
-"&copy; Crown copyright. Alle rettigheter reservert. Justisdepartementet "
-"100037819&nbsp;2008."
+"Bygget av <a href=\"http://www.mysociety.org/\">mySociety</a>, ved hjelp av "
+"<a href=\"http://github.com/mysociety/fixmystreet\">smart</a>&nbsp;<a href="
+"\"https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa"
+"\">kode</a>."
-#: perllib/FixMyStreet/Map/Tilma/Original.pm:86
-msgid "Unable to fetch the map tiles from the tile server."
-msgstr "Klarte ikke hente kartfliser fra filtjeneren."
+#: templates/web/default/report/new/fill_in_details.html:141
+msgid "Can we show your name on the site?"
+msgstr "Kan vi vise ditt navn her på nettstedet?"
-#: perllib/Problems.pm:148
-#, perl-format
-msgid "<big>%s</big> report in past week"
-msgid_plural "<big>%s</big> reports in past week"
-msgstr[0] "<big>%s</big> rapport siste uke"
-msgstr[1] "<big>%s</big> rapporter siste uke"
+#: templates/web/default/report/display.html:87
+#: templates/web/default/report/new/fill_in_details.html:143
+msgid "Can we show your name publicly?"
+msgstr "Kan vi vise ditt navn offentlig?"
-#: perllib/Problems.pm:153
-#, perl-format
-msgid "<big>%s</big> report recently"
-msgid_plural "<big>%s</big> reports recently"
-msgstr[0] "<big>%s</big> rapportert<br>nylig"
-msgstr[1] "<big>%s</big> rapportert<br>nylig"
+#: templates/web/default/admin/council_contacts.html:19
+#: templates/web/default/admin/search_reports.html:17
+msgid "Category"
+msgstr "Kategori"
-#: perllib/Problems.pm:159
-msgid "FixMyStreet updates"
-msgstr "Fiksgatami-oppdateringer"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:478
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:502
+#: templates/web/default/admin/council_contacts.html:56
+#: templates/web/default/admin/council_edit.html:23
+#: templates/web/default/admin/report_edit.html:26
+msgid "Category:"
+msgstr "Kategori:"
-#: perllib/Problems.pm:162 perllib/Problems.pm:166
-#, perl-format
-msgid "<big>%s</big> fixed in past month"
-msgid_plural "<big>%s</big> fixed in past month"
-msgstr[0] "<big>%s</big> fikset siste måned"
-msgstr[1] "<big>%s</big> fikset siste måned"
+#: bin/send-reports:170
+msgid "Category: %s"
+msgstr "Kategori: %s"
-#: perllib/Problems.pm:167
-#, perl-format
-msgid "<big>%s</big> update on reports"
-msgid_plural "<big>%s</big> updates on reports"
-msgstr[0] "<big>%s</big> rapport-<br>oppdatering"
-msgstr[1] "<big>%s</big> rapport-<br>oppdateringer"
+#: templates/web/default/auth/change_password.html:1
+#: templates/web/default/auth/change_password.html:3
+#: templates/web/default/auth/change_password.html:35
+msgid "Change Password"
+msgstr ""
-#: web/about.cgi:24
-msgid ""
-"<h2>The Empty Homes Agency</h2>\n"
-"<p>The Empty Homes agency is an independent campaigning charity. We are not\n"
-"part of government, and have no formal links with local councils although "
-"we\n"
-"work in cooperation with both. We exist to highlight the waste of empty\n"
-"property and work with others to devise and promote sustainable solutions "
-"to\n"
-"bring empty property back into use. We are based in London but work across\n"
-"England. We also work in partnership with other charities across the UK.</"
-"p>\n"
-msgstr ""
-
-#: web/about.cgi:34
-msgid ""
-"<h2>Shelter Cymru</h2>\n"
-"Shelter Cymru is Wales&rsquo; people and homes charity and wants everyone in "
-"Wales to\n"
-"have a decent home. We believe a home is a fundamental right and essential "
-"to\n"
-"the health and well-being of people and communities. We work for people in\n"
-"housing need. We have offices all over Wales and prevent people from losing\n"
-"their homes by offering free, confidential and independent advice. When\n"
-"necessary we constructively challenge on behalf of people to ensure they "
-"are\n"
-"properly assisted and to improve practice and learning. We believe that\n"
-"bringing empty homes back into use can make a significant contribution to "
-"the\n"
-"supply of affordable homes in Wales.\n"
-"<a href=\"http://www.sheltercymru.org.uk/shelter/advice/pdetail.asp?cat=20"
-"\">Further information about our work on\n"
-"empty homes</a>.\n"
-msgstr ""
-
-#: web/ajax.cgi:55 web/ajax.cgi:76 web/index.cgi:935 web/index.cgi:950
-msgid "(fixed)"
-msgstr "(løst)"
+#: templates/web/default/around/display_location.html:111
+#: templates/web/default/around/display_location.html:113
+msgid "Closest nearby problems <small>(within&nbsp;%skm)</small>"
+msgstr "De nærmeste sakene <small>(innenfor&nbsp;%skm)</small>"
-#: web/alert.cgi:33 web/confirm.cgi:49
-msgid "Confirmation"
-msgstr "Bekreftelse"
+#: templates/web/default/admin/report_edit.html:15
+msgid "Co-ordinates:"
+msgstr "Koordinater:"
-#: web/alert.cgi:43
-#, perl-format
-msgid ""
-"Thank you for trying to confirm your alert. We seem to have an error "
-"ourselves\n"
-"though, so <a href=\"%s\">please let us know what went on</a> and we'll look "
-"into it.\n"
+#: templates/web/default/admin/list_updates.html:11
+#: templates/web/default/admin/search_reports.html:19
+msgid "Cobrand"
msgstr ""
-"Takk for at du forsøker å bekrefte ditt varsel. Vi ser ut til a ha en feil "
-"hos oss,\n"
-"så <a href=\"%s\">vær så snill å fortell oss hva som skjedde</a> så skal vi "
-"se på saken.\n"
-#: web/alert.cgi:59 web/alert.cgi:62 web/alert.cgi:65 web/alert.cgi:274
-#: web/alert.cgi:380
-msgid "Local RSS feeds and email alerts"
-msgstr "Lokal RSS-strøm og epostvarsel"
+#: templates/web/default/admin/report_edit.html:36
+#: templates/web/default/admin/update_edit.html:26
+msgid "Cobrand data:"
+msgstr ""
-#: web/alert.cgi:121
-msgid ""
-"That location does not appear to be covered by a council, perhaps it is "
-"offshore - please try somewhere more specific."
+#: templates/web/default/admin/report_edit.html:35
+#: templates/web/default/admin/update_edit.html:25
+msgid "Cobrand:"
msgstr ""
-"Det stedet dekkes ikke av noen administrasjon, kanskje det er til havs - "
-"vennligst forsøk et mer spesifikt sted."
-#: web/alert.cgi:140 web/alert.cgi:183 web/alert.cgi:200
-#, perl-format
-msgid "Problems within %s"
-msgstr "Problemer innenfor %s"
+#: templates/web/default/admin/council_contacts.html:26
+msgid "Confirm"
+msgstr "Bekreftet"
-#: web/alert.cgi:152 web/alert.cgi:233
-msgid "Problems within the boundary of:"
-msgstr "Problemer innenfor grensene av:"
+#: templates/web/default/auth/token.html:1
+#, fuzzy
+msgid "Confirm account"
+msgstr "Bekreftelse"
-#: web/alert.cgi:158 web/alert.cgi:245
-msgid "Or problems reported to:"
-msgstr "Eller problemer meldt til:"
+#: templates/web/default/questionnaire/creator_fixed.html:1
+#: templates/web/default/tokens/confirm_problem.html:1
+#: templates/web/default/tokens/confirm_problem.html:3
+#: templates/web/default/tokens/confirm_update.html:1
+#: templates/web/default/tokens/confirm_update.html:3
+#: templates/web/emptyhomes/tokens/confirm_problem.html:1
+#: templates/web/emptyhomes/tokens/confirm_problem.html:3
+msgid "Confirmation"
+msgstr "Bekreftelse"
-#: web/alert.cgi:160 web/alert.cgi:247
-msgid ""
-"FixMyStreet sends different categories of problem\n"
-"to the appropriate council, so problems within the boundary of a particular "
-"council\n"
-"might not match the problems sent to that council. For example, a graffiti "
-"report\n"
-"will be sent to the district council, so will appear in both of the "
-"district\n"
-"council&rsquo;s alerts, but will only appear in the \"Within the boundary\" "
-"alert\n"
-"for the county council."
-msgstr ""
-"FiksGataMi sender forskjellige kategorier problemer til forskjellige "
-"administrasjoner, problemer som gjelder flere administrasjoner blir sendt "
-"til alle de det gjelder."
+#: templates/web/default/admin/council_contacts.html:21
+#: templates/web/default/admin/council_contacts.html:66
+#: templates/web/default/admin/council_edit.html:28
+#: templates/web/default/admin/council_edit.html:43
+msgid "Confirmed"
+msgstr "Bekreftet"
-#: web/alert.cgi:185
-#, perl-format
-msgid "Problems within %s ward"
-msgstr "Problemer innenfor %s avdeling/etat"
+#: templates/web/default/admin/report_edit.html:31
+#: templates/web/default/admin/search_reports.html:47
+msgid "Confirmed:"
+msgstr "Bekreftet:"
-#: web/alert.cgi:269
-msgid "Photos of recent nearby reports"
-msgstr "Bilder av nye saker i nærheten"
+#: templates/web/default/footer.html:10 templates/web/fiksgatami/footer.html:9
+msgid "Contact"
+msgstr "Kontakt"
-#: web/alert.cgi:272
-#, perl-format
-msgid "Local RSS feeds and email alerts for &lsquo;%s&rsquo;"
-msgstr "Lokal RSS-strøm og epostvarsel for &lsquo;%s&rsquo;"
+#: templates/web/default/contact/index.html:1
+#: templates/web/default/contact/index.html:2
+#: templates/web/default/contact/submit.html:1
+msgid "Contact Us"
+msgstr "Kontakt oss"
-#: web/alert.cgi:286
-#, perl-format
-msgid "Here are the types of local problem alerts for &lsquo;%s&rsquo;."
-msgstr "Her er typene lokale problemvarsler for &lsquo;%s&rsquo;."
+#: templates/web/default/contact/index.html:6
+#: templates/web/default/contact/submit.html:3
+msgid "Contact the team"
+msgstr "Kontakt prosjektgruppen"
-#: web/alert.cgi:287
-msgid ""
-"Select which type of alert you&rsquo;d like and click the button for an RSS\n"
-"feed, or enter your email address to subscribe to an email alert."
+#: templates/web/default/admin/search_reports.html:16
+msgid "Council"
+msgstr "Administrasjon"
+
+#: perllib/FixMyStreet/App/Controller/Admin.pm:598
+#: templates/web/default/admin/council_list.html:1
+msgid "Council contacts"
+msgstr "Administrasjonskontakter"
+
+#: templates/web/default/admin/council_contacts.html:1
+#: templates/web/default/admin/council_edit.html:1
+msgid "Council contacts for %s"
+msgstr "Administrasjonskontakter for %s"
+
+#: templates/web/default/admin/council_list.html:36
+msgid "Councils"
+msgstr "Administrasjoner"
+
+#: templates/web/default/email_sent.html:1
+#, fuzzy
+msgid "Create a report"
+msgstr "Lag kategori"
+
+#: templates/web/default/admin/council_contacts.html:80
+msgid "Create category"
+msgstr "Lag kategori"
+
+#: templates/web/default/admin/list_updates.html:9
+#: templates/web/default/admin/search_reports.html:20
+msgid "Created"
+msgstr "Opprettet"
+
+#: templates/web/default/admin/report_edit.html:30
+#: templates/web/default/admin/update_edit.html:27
+msgid "Created:"
+msgstr "Opprettet:"
+
+#: templates/web/default/admin/council_list.html:40
+msgid "Currently has 1+ deleted"
+msgstr "For tiden har 1+ slettet"
+
+#: templates/web/default/admin/council_contacts.html:22
+#: templates/web/default/admin/council_contacts.html:69
+#: templates/web/default/admin/council_edit.html:29
+#: templates/web/default/admin/council_edit.html:44
+msgid "Deleted"
+msgstr "Slettet"
+
+#: templates/web/default/admin/report_edit.html:14
+#: templates/web/default/report/new/fill_in_details.html:100
+msgid "Details:"
+msgstr "Detaljer:"
+
+#: templates/web/default/admin/council_list.html:23
+msgid "Diligency prize league table"
+msgstr "Hvem har endret kontaktlista"
+
+#: templates/web/default/questionnaire/index.html:72
+msgid "Don&rsquo;t know"
+msgstr "Vet ikke"
+
+#: templates/web/default/admin/list_updates.html:31
+#: templates/web/default/admin/search_reports.html:52
+msgid "Edit"
+msgstr "Rediger"
+
+#: templates/web/default/admin/report_edit.html:1
+msgid "Editing problem %d"
+msgstr "Rediger problem %d"
+
+#: templates/web/default/admin/update_edit.html:1
+msgid "Editing update %d"
+msgstr "Redigerer oppdatering %d"
+
+#: templates/web/default/admin/council_edit.html:45
+msgid "Editor"
+msgstr "Oppdatert av"
+
+#: templates/web/default/admin/council_contacts.html:20
+#: templates/web/default/admin/council_edit.html:42
+#: templates/web/default/admin/list_updates.html:8
+#: templates/web/default/admin/search_reports.html:15
+#: templates/web/default/report/display.html:95
+msgid "Email"
+msgstr "Epost"
+
+#: templates/web/default/around/display_location.html:77
+msgid "Email me new local problems"
+msgstr "Send meg epost om lokale problemer"
+
+#: templates/web/default/report/display.html:42
+msgid "Email me updates"
+msgstr "Send meg oppdateringer"
+
+#: templates/web/default/auth/general.html:47
+msgid "Email the details I need to the address I entered above"
msgstr ""
-"Velg hvilken type varsel du ønsker og klikk på knappen for en RSS-strøm, "
-"eller skriv inn din epostadresse for å abonnere på et epostvarsel."
-#: web/alert.cgi:290
-msgid "The simplest alert is our geographic one:"
-msgstr "Den enkleste meldingen er vår geografiske:"
+#: templates/web/default/admin/council_contacts.html:61
+#: templates/web/default/admin/council_edit.html:26
+#: templates/web/default/admin/report_edit.html:28
+#: templates/web/default/admin/update_edit.html:24
+#: templates/web/default/alert/updates.html:13
+#: templates/web/default/auth/general.html:29
+#: templates/web/default/report/display.html:46
+#: templates/web/default/report/new/fill_in_details.html:153
+msgid "Email:"
+msgstr "Epost:"
-#: web/alert.cgi:291
-#, perl-format
-msgid "Problems within %skm of this location"
-msgstr "Saker innen %skm av dette punktet"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:463
+msgid "Empty flat or maisonette"
+msgstr ""
-#: web/alert.cgi:305
-msgid "(a default distance which covers roughly 200,000 people)"
-msgstr "(en default-avstand som dekker en befolkning på omtrent 200 000)"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:462
+msgid "Empty house or bungalow"
+msgstr "Tomt hus eller bungalow"
-#: web/alert.cgi:308
-msgid "RSS feed of nearby problems"
-msgstr "RSS-strøm med problemer i nærheten"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:465
+msgid "Empty office or other commercial"
+msgstr "Tomt kontor eller forretningsbygg"
-#: web/alert.cgi:308 web/alert.cgi:367 web/index.cgi:1005 web/index.cgi:1117
-#: web/reports.cgi:254
-msgid "RSS feed"
-msgstr "RSS-strøm"
+#: templates/web/emptyhomes/report/new/form_heading.html:1
+msgid "Empty property details form"
+msgstr "Tom eiendom detaljskjema"
-#: web/alert.cgi:309
-msgid "(alternatively the RSS feed can be customised, within"
-msgstr "(alternativt kan RSS-strømmen tilpasses, innenfor"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:466
+msgid "Empty pub or bar"
+msgstr "Tom pub eller bar"
-#: web/alert.cgi:319
-msgid ""
-"Or you can subscribe to an alert based upon what ward or council you&rsquo;"
-"re in:"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:467
+msgid "Empty public building - school, hospital, etc."
+msgstr "Tom offentlig bygning - skole, sykehos, etc."
+
+#: templates/web/default/around/around_index.html:10
+#: templates/web/default/around/around_index.html:13
+#: templates/web/default/index.html:40 templates/web/default/index.html:43
+msgid "Enter a nearby GB postcode, or street name and area"
+msgstr "Skriv inn GB-postnummer i nærheten, eller veinavn og sted"
+
+#: perllib/FixMyStreet/Cobrand/FiksGataMi.pm:20
+msgid "Enter a nearby postcode, or street name and area"
+msgstr "Skriv inn postnummer i nærheten, eller veinavn og sted"
+
+#: templates/web/default/index.html:61
+msgid "Enter details of the problem"
+msgstr "Legg inn detaljer om problemet"
+
+#: templates/web/default/auth/token.html:5
+#: templates/web/default/questionnaire/error.html:1
+#: templates/web/default/questionnaire/error.html:3
+#: templates/web/default/tokens/abuse.html:1
+#: templates/web/default/tokens/abuse.html:3
+#: templates/web/default/tokens/error.html:1
+#: templates/web/default/tokens/error.html:3
+msgid "Error"
+msgstr "Feil"
+
+#: templates/web/default/admin/council_contacts.html:9
+#: templates/web/default/admin/council_edit.html:18
+msgid "Example postcode %s"
msgstr ""
-"Eller du kan abonnere på varsel basert på bydel eller administrasjon du "
-"hører inn under:"
-#: web/alert.cgi:323
-msgid "Give me an RSS feed"
-msgstr "Gi meg en RSS-strøm"
+#: templates/web/default/contact/submit.html:15
+#, fuzzy
+msgid ""
+"Failed to send message. Please try again, or <a href=\"mailto:%s\">email "
+"us</a>."
+msgstr ""
+"Vær så snill å forsøk igjen senere, eller <a href=\"mailto:%s\">send oss en "
+"epost</a> og gi oss beskjed."
-#: web/alert.cgi:324
-msgid "or"
-msgstr "eller"
+#: templates/web/default/questionnaire/index.html:81
+msgid "First time"
+msgstr "Første gang"
-#: web/alert.cgi:325
-msgid "Your email:"
-msgstr "Din epost"
+#: templates/web/default/header.html:16
+#: templates/web/fiksgatami/header.html:14
+msgid "Fix<span id=\"my\">My</span>Street"
+msgstr "Fiks<span id=\"my\">Gata</span>Mi"
-#: web/alert.cgi:326
-msgid "Subscribe me to an email alert"
-msgstr "Jeg ønsker å abonnere på epostvarsel"
+#: templates/web/default/admin/header.html:12
+msgid "FixMyStreet admin:"
+msgstr "FiksGataMi-admin:"
-#: web/alert.cgi:367
-#, perl-format
-msgid "RSS feed of %s"
-msgstr "RSS-strøm fra %s"
+#: templates/web/default/admin/header.html:3
+msgid "FixMyStreet administration"
+msgstr "Fiksgatami-administrasjon"
-#: web/alert.cgi:381
+#: templates/web/default/alert/index.html:6
msgid ""
"FixMyStreet has a variety of RSS feeds and email alerts for local problems, "
"including\n"
@@ -656,651 +704,660 @@ msgstr ""
"et område med problemer\n"
"innen en angitt distanse fra en bestemt posisjon."
-#: web/alert.cgi:384
+#: templates/web/default/alert/list.html:101
msgid ""
-"To find out what local alerts we have for you, please enter your GB\n"
-"postcode or street name and area:"
+"FixMyStreet sends different categories of problem\n"
+"to the appropriate council, so problems within the boundary of a particular "
+"council\n"
+"might not match the problems sent to that council. For example, a graffiti "
+"report\n"
+"will be sent to the district council, so will appear in both of the "
+"district\n"
+"council&rsquo;s alerts, but will only appear in the \"Within the boundary\" "
+"alert\n"
+"for the county council."
msgstr ""
-"Du finner lokale saker ved å søke på ditt postnummer, veinavn eller sted:"
-
-#: web/alert.cgi:389 web/index.cgi:167
-msgid "Go"
-msgstr "Fortsett"
+"FiksGataMi sender forskjellige kategorier problemer til forskjellige "
+"administrasjoner, problemer som gjelder flere administrasjoner blir sendt "
+"til alle de det gjelder."
-#: web/alert.cgi:415
-msgid "Some photos of recent reports"
-msgstr "Noen bilder av nylig meldte saker"
+#: templates/web/default/front_stats.html:7
+msgid "FixMyStreet updates"
+msgstr "Fiksgatami-oppdateringer"
-#: web/alert.cgi:423
-msgid "Please select the feed you want"
-msgstr "Velg den feed du ønsker"
+#: templates/web/default/admin/report_edit.html:22
+msgid "Fixed"
+msgstr "Løst"
-#: web/alert.cgi:446
-msgid "Illegal feed selection"
-msgstr "Ugyldig valg av feed"
+#: templates/web/default/admin/search_reports.html:48
+msgid "Fixed:"
+msgstr "Løst:"
-#: web/alert.cgi:460
-msgid "Receive email when updates are left on this problem."
-msgstr "Motta epost når det er uppdateringer på denne saken"
+#: templates/web/default/admin/report_edit.html:16
+msgid "For council(s):"
+msgstr "For administrasjon(er):"
-#: web/alert.cgi:461 web/alert.cgi:488 web/index.cgi:769 web/index.cgi:1111
-#: web-admin/index.cgi:631 web-admin/index.cgi:751
-msgid "Email:"
-msgstr "Epost:"
+#: templates/web/default/faq/faq-en-gb.html:1
+#: templates/web/emptyhomes/faq/faq-cy.html:1
+#: templates/web/emptyhomes/faq/faq-en-gb.html:1
+#: templates/web/fiksgatami/faq/faq-nb.html:1
+msgid "Frequently Asked Questions"
+msgstr "Ofte spurte spørsmål"
-#: web/alert.cgi:462 web/alert.cgi:489 web/index.cgi:1112
-msgid "Subscribe"
-msgstr "Abonner"
+#: templates/web/emptyhomes/static/about.html:34
+msgid "Further information about our work on empty homes."
+msgstr ""
-#: web/alert.cgi:487
-msgid "Receive alerts on new local problems"
-msgstr "Motta varsel om nye lokale problemer"
+#: templates/web/default/index.html:22
+#, fuzzy
+msgid "Get FixMyStreet on your iPhone"
+msgstr "Fiksgatami-administrasjon"
-#: web/alert.cgi:515
-msgid "You have successfully subscribed to that alert."
-msgstr "Du abonnerer nå på denne saken"
+#: templates/web/default/alert/list.html:115
+msgid "Give me an RSS feed"
+msgstr "Gi meg en RSS-strøm"
-#: web/alert.cgi:522
-msgid "We could not validate that alert."
-msgstr "Vi kunne ikke validere det varselet."
+#: templates/web/default/alert/index.html:24
+#: templates/web/default/around/around_index.html:17
+#: templates/web/default/index.html:49
+msgid "Go"
+msgstr "Fortsett"
-#: web/alert.cgi:544
-msgid "You have successfully confirmed your alert."
-msgstr "Du har nå lykkes med å bekrefte ditt varsel."
+#: templates/web/default/admin/report_edit.html:37
+msgid "Going to send questionnaire?"
+msgstr "Skal det sendes spørreskjema?"
-#: web/alert.cgi:551
-msgid "You have successfully deleted your alert."
-msgstr "Sletting av ditt varsel var vellykket."
+#: templates/web/default/admin/index.html:23
+msgid "Graph of problem creation by status over time"
+msgstr "Graf over problemoppretting fordelt på status over tid"
-#: web/alert.cgi:571
-msgid "Please enter a valid email address"
-msgstr "Legg inn din epost"
+#: templates/web/default/reports/index.html:5
+#: templates/web/emptyhomes/reports/index.html:5
+msgid "Greyed-out lines are councils that no longer exist."
+msgstr ""
+"Linjer med grå bakgrunn er administrasjoner som ikke lenger eksisterer."
-#: web/alert.cgi:572
-msgid "Please select the type of alert you want"
-msgstr "Velg vilken type varsel du ønsker"
+#: templates/web/default/questionnaire/index.html:63
+msgid "Has this problem been fixed?"
+msgstr "Har denne saken blitt løst?"
-#: web/confirm.cgi:39
-#, perl-format
+#: templates/web/default/questionnaire/index.html:76
msgid ""
-"Thank you for trying to confirm your update or problem. We seem to have an\n"
-"error ourselves though, so <a href=\"%s\">please let us know what went on</"
-"a>\n"
-"and we'll look into it.\n"
+"Have you ever reported a problem to a council before, or is this your first "
+"time?"
msgstr ""
-"Takk for at du forsøker å bekrefte din oppdatering eller ditt problem. Vi "
-"ser ut til å ha en\n"
-"feil hos oss, så <a href=\"%s\">vær så snill å fortell oss hva som skjedde</"
-"a>\n"
-"så skal vi se på saken.\n"
+"Har du sendt en sak til en administrasjon før, eller er dette første gangen?"
-#: web/confirm.cgi:101
-#, perl-format
-msgid ""
-"You have successfully confirmed your update and you can now <a href=\"%s"
-"\">view it on the site</a>."
-msgstr ""
-"Du har nå bekreftet din oppdatering <a href=\"%s\">les om saken på portalen</"
-"a>."
+#: templates/web/default/footer.html:9 templates/web/emptyhomes/header.html:33
+#: templates/web/fiksgatami/footer.html:8
+msgid "Help"
+msgstr "Hjelp"
-#: web/confirm.cgi:131
-msgid "Sorry, there has been an error confirming your problem."
-msgstr "Beklager, det har oppstått problemer når vi under lagringen av saken"
+#: templates/web/default/alert/list.html:39
+msgid "Here are the types of local problem alerts for &lsquo;%s&rsquo;."
+msgstr "Her er typene lokale problemvarsler for &lsquo;%s&rsquo;."
-#: web/confirm.cgi:139
-msgid ""
-"Thank you for reporting an empty property on\n"
-"ReportEmptyHomes.com. We have emailed the lead officer for empty homes in "
-"the council\n"
-"responsible with details, and asked them to do whatever they can to get the\n"
-"empty property back into use as soon as possible."
-msgstr ""
+#: templates/web/default/admin/report_edit.html:22
+#: templates/web/default/admin/update_edit.html:19
+msgid "Hidden"
+msgstr "Gjemt"
+
+#: templates/web/default/around/display_location.html:54
+msgid "Hide pins"
+msgstr "Gjem nåler"
+
+#: templates/web/default/around/display_location.html:59
+msgid "Hide stale reports"
+msgstr "Gjem utdaterte rapporter"
-#: web/confirm.cgi:143
+#: templates/web/default/admin/council_edit.html:38
+msgid "History"
+msgstr "Historie"
+
+#: templates/web/default/index.html:56
+msgid "How to report a problem"
+msgstr "Hvordan rapportere et problem"
+
+#: perllib/FixMyStreet/App/Controller/Admin.pm:516
+msgid "I am afraid you cannot confirm unconfirmed reports."
+msgstr "Jeg er redd du ikke kan bekrefte ubekreftede rapporter."
+
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:93
+msgid "I'm afraid we couldn't locate your problem in the database.\n"
+msgstr "Jeg er redd vi ikke klarte å finne ditt problem i databasen.\n"
+
+#: perllib/FixMyStreet/App/Controller/Tokens.pm:161
msgid ""
-"It is worth noting however that the process can sometimes be slow,\n"
-"especially if the property is in very poor repair or the owner is unwilling "
-"to\n"
-"act. In most cases it can take six months or more before you can expect to "
-"see\n"
-"anything change and sometimes there may be considerable barries to a "
-"property\n"
-"being brought back into use. This doesn&rsquo;t mean the council isn&rsquo;"
-"t\n"
-"doing anything. We encourage councils to update the website so you can\n"
-"see what is happening. It may be a long process, but you reporting your\n"
-"concerns about this property to the council is a valuable first step."
+"I'm afraid we couldn't validate that token. If you've copied the URL from an "
+"email, please check that you copied it exactly.\n"
msgstr ""
+"Jeg er redd vi ikke kunne verifisere den referansen. Hvis du kopierte den "
+"URLen fra en annen epost, sjekk at du har kopiert den korrekt.\n"
+
+#: templates/web/default/admin/list_updates.html:5
+#: templates/web/default/admin/search_reports.html:12
+msgid "ID"
+msgstr "ID"
-#: web/confirm.cgi:151
+#: templates/web/default/report/new/no_councils_text.html:11
+#: templates/web/default/report/new/no_councils_text.html:3
msgid ""
-"We may contact you periodically to ask if anything has changed\n"
-"with the property you reported."
+"If you submit a problem here the subject and details of the problem will be "
+"public, but the problem will <strong>not</strong> be reported to the council."
msgstr ""
-"Det kan hende vi periodisk tar kontakt med deg for å spørre om noe har "
-"endret seg med eiedommen du rapporterte."
+"Hvis du sender inn et problem hit, så vil tema og detaljer for problemet "
+"være offentlig, men problemet vil <strong>ikke</strong> bli rapportert til "
+"administrasjonen."
-#: web/confirm.cgi:153
+#: templates/web/emptyhomes/report/new/no_councils_text.html:9
msgid ""
-"Thank you for using ReportEmptyHomes.com. Your action is already helping\n"
-"to resolve the UK&rsquo;s empty homes crisis."
+"If you submit a report here it will be left on the site, but not reported to "
+"the council &ndash; please still leave your report, so that we can show to "
+"the council the activity in their area."
msgstr ""
+"Hvis du sender inn en rapport her så vil den bli tilgjengelig her men ikke "
+"rapportert til administrasjonen. &ndash; det er fint om du likevel sender "
+"inn din rapport, slik at vi kan vise administrasjonen aktiviteten i deres "
+"område."
-#: web/confirm.cgi:155 web/confirm.cgi:161
-msgid "View your report"
-msgstr "Vis din rapport"
-
-#: web/confirm.cgi:157
+#: templates/web/default/email_sent.html:28
msgid ""
-"Thank you for reporting this empty property on ReportEmptyHomes.com.\n"
-"At present the report cannot be sent through to the council for this area. "
-"We\n"
-"are working with councils to link them into the system so that as many "
-"areas\n"
-"as possible will be covered."
+"If you use web-based email or have 'junk mail' filters, you may wish to "
+"check your bulk/spam mail folders: sometimes, our messages are marked that "
+"way."
msgstr ""
-#: web/confirm.cgi:166
-msgid "You have successfully confirmed your problem"
-msgstr "Du har nå oppdatert din sak"
-
-#: web/confirm.cgi:167
-msgid " and <strong>we will now send it to the council</strong>"
-msgstr " og <strong>vi sender det nå til administrasjonen</strong>"
-
-#: web/confirm.cgi:168
-#, perl-format
-msgid ". You can <a href=\"%s\">view the problem on this site</a>."
-msgstr ". Du kan <a href=\"%s\">lese saken på portalen </a>."
-
-#: web/confirm.cgi:192
+#: templates/web/default/questionnaire/index.html:85
msgid ""
-"Thanks, glad to hear it's been fixed! Could we just ask if you have ever "
-"reported a problem to a council before?"
+"If you wish to leave a public update on the problem, please enter it here\n"
+"(please note it will not be sent to the council). For example, what was\n"
+"your experience of getting the problem fixed?"
msgstr ""
-"Takk, glad for å høre at problemet er fikset! Vi vil gjerne spørre deg om "
-"du har rapportert et problem til en administrasjon tidligere?"
+"Hvis du ønsker å legge til en offentlig kommentar til saken, legg den til "
+"her\n"
+"(denne blir sendt til administrasjonen). Du kan for eksempel\n"
+"dele din erfaring med hvordan din sak ble løst."
-#: web/confirm.cgi:193 web/questionnaire.cgi:234 web/questionnaire.cgi:323
-#: web-admin/index.cgi:341 web-admin/index.cgi:342 web-admin/index.cgi:504
-#: web-admin/index.cgi:591 web-admin/index.cgi:609
-msgid "Yes"
-msgstr "Ja"
+#: perllib/FixMyStreet/App/Controller/Contact.pm:109
+msgid "Illegal ID"
+msgstr "Ugyldig ID"
-#: web/confirm.cgi:194 web/questionnaire.cgi:235 web/questionnaire.cgi:324
-#: web-admin/index.cgi:341 web-admin/index.cgi:342 web-admin/index.cgi:504
-#: web-admin/index.cgi:591 web-admin/index.cgi:609
-msgid "No"
-msgstr "Nei"
+#: perllib/FixMyStreet/App/Controller/Alert.pm:105
+msgid "Illegal feed selection"
+msgstr "Ugyldig valg av feed"
-#: web/confirm.cgi:195 web/index.cgi:821
-msgid "Submit"
-msgstr "Send inn"
+#: templates/web/default/around/display_location.html:61
+msgid "Include stale reports"
+msgstr "Inkludert utdaterte rapporter"
-#: web/confirm.cgi:240
-#, perl-format
+#: templates/web/emptyhomes/tokens/confirm_problem.html:12
+#: templates/web/emptyhomes/tokens/confirm_problem.html:14
msgid ""
-"Thank you &mdash; you can <a href=\"%s\">view your updated problem</a> on "
-"the site."
+"It is worth noting however that the process can sometimes be slow, "
+"especially if the property is in very poor repair or the owner is unwilling "
+"to act. In most cases it can take six months or more before you can expect "
+"to see anything change and sometimes there may be considerable barries to a "
+"property being brought back into use. This doesn&rsquo;t mean the council "
+"isn&rsquo;t doing anything. We encourage councils to update the website so "
+"you can see what is happening. It may be a long process, but you reporting "
+"your concerns about this property to the council is a valuable first step."
msgstr ""
-"Tusen takk &mdash; du kan <a href=\"%s\">se på ditt oppdaterte problem</a> "
-"her hos oss."
-#: web/contact.cgi:22
-msgid "Contact Us"
-msgstr "Kontakt oss"
+#: templates/web/default/admin/council_contacts.html:23
+msgid "Last editor"
+msgstr "Sist redigert av"
-#: web/contact.cgi:41
-msgid "Please give your name"
-msgstr "Skriv inn ditt navn"
+#: templates/web/default/admin/report_edit.html:33
+msgid "Last update:"
+msgstr "Siste oppdatering:"
-#: web/contact.cgi:43
-msgid "Please give your email"
-msgstr "Skriv din epost"
+#: templates/web/default/admin/search_reports.html:49
+msgid "Last&nbsp;update:"
+msgstr "Last&nbsp;oppdatering:"
-#: web/contact.cgi:45
-msgid "Please give a valid email address"
-msgstr "Skriv en gyldig epostadresse"
+#: templates/web/default/admin/council_contacts.html:11
+#, fuzzy
+msgid "List all reported problems"
+msgstr " List alle rapporterte problemer"
-#: web/contact.cgi:47
-msgid "Please give a subject"
-msgstr "Skriv inn emne"
+#: templates/web/default/alert/choose.html:1
+#: templates/web/default/alert/choose.html:3
+#: templates/web/default/alert/index.html:1
+#: templates/web/default/alert/index.html:3
+#: templates/web/default/alert/list.html:1
+#: templates/web/default/alert/list.html:5
+#: templates/web/default/alert/updates.html:1
+#: templates/web/default/tokens/confirm_alert.html:1
+#: templates/web/default/tokens/confirm_alert.html:3
+msgid "Local RSS feeds and email alerts"
+msgstr "Lokal RSS-strøm og epostvarsel"
-#: web/contact.cgi:48
-msgid "Please write a message"
-msgstr "Skriv inn en melding"
+#: templates/web/default/alert/list.html:1
+#: templates/web/default/alert/list.html:12
+#: templates/web/default/alert/list.html:14
+#: templates/web/default/alert/list.html:3
+#, fuzzy
+msgid "Local RSS feeds and email alerts for ‘%s’"
+msgstr "Lokal RSS-strøm og epostvarsel"
-#: web/contact.cgi:49
-msgid "Illegal ID"
-msgstr "Ugyldig ID"
+#: templates/web/default/footer.html:8 templates/web/emptyhomes/header.html:32
+#: templates/web/fiksgatami/footer.html:7
+msgid "Local alerts"
+msgstr "Lokale varsler"
-#: web/contact.cgi:86
-msgid "Thanks for your feedback. We'll get back to you as soon as we can!"
+#: templates/web/default/index.html:60
+msgid "Locate the problem on a map of the area"
+msgstr "Lokaliser problemet på kartet over området"
+
+#: templates/web/default/auth/general.html:43
+msgid "Log me in"
msgstr ""
-"Takk for ditt innspill. Vi gir deg en tilbakemelding så raskt som mulig"
-#: web/contact.cgi:134
-msgid ""
-"We&rsquo;d love to hear what you think about this\n"
-"website. Just fill in the form. Please don&rsquo;t contact us about "
-"individual empty\n"
-"homes; use the box accessed from <a href=\"/\">the front page</a>."
+#: templates/web/default/auth/general.html:1
+#: templates/web/default/auth/general.html:3
+msgid "Login or create an account"
msgstr ""
-"Vi vil gjerne høre hva du tenker om dette nettstedet. Det er bare å fylle "
-"inn skjemaet. Vær så snill å ikke kontakte oss om individuelle tomme hjem. "
-"For det bør du i stedet bruke boksen tilgjengelig fra <a href=\"/"
-"\">forsiden</a>."
-#: web/contact.cgi:141
-msgid ""
-"Please do <strong>not</strong> report problems through this form; messages "
-"go to\n"
-"the team behind FixMyStreet, not a council. To report a problem,\n"
-"please <a href=\"/\">go to the front page</a> and follow the instructions."
+#: templates/web/default/auth/logout.html:1
+msgid "Logout"
msgstr ""
-"Vennligst <strong>ikke</strong> rapporter feil gjennom denne siden; "
-"meldinger går til\n"
-"gruppen som står bak fiksgatami, og ikke til en administrasjon. For å "
-"rapportere et problem,\n"
-"vennligst <a href=\"/\">gå til forsiden</a> og følg instruksjonene."
-#: web/contact.cgi:144
-#, perl-format
+#: perllib/FixMyStreet/Map/OSM.pm:43
msgid ""
-"We'd love to hear what you think about this site. Just fill in the form, or "
-"send an email to <a href='mailto:%s'>%s</a>:"
+"Map &copy; <a id=\"osm_link\" href=\"http://www.openstreetmap.org/"
+"\">OpenStreetMap</a> and contributors, <a href=\"http://creativecommons.org/"
+"licenses/by-sa/2.0/\">CC-BY-SA</a>"
msgstr ""
-"Vi ønsker å få din tilbakemelding om hva du mener om denne tjenesten. Bare "
-"fyll ut skjemaet, eller send en epost <a href='mailto:%s'>%s</a>:"
-
-#: web/contact.cgi:153 web/index.cgi:463
-msgid "There were problems with your report. Please see below."
-msgstr "Det var problemer med din rapport. Vennligst se under."
-
-#: web/contact.cgi:158
-msgid "Contact the team"
-msgstr "Kontakt prosjektgruppen"
+"Kart &copy; <a id=\"osm_link\" href=\"http://www.openstreetmap.org/"
+"\">OpenStreetMap</a> og bidragsytere, <a href=\"http://creativecommons.org/"
+"licenses/by-sa/2.0/\">CC-BY-SA</a>"
-#: web/contact.cgi:196
+#: perllib/FixMyStreet/Map/OSM/StreetView.pm:27
+#: perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm:21
+#: perllib/FixMyStreet/Map/Tilma/Original/StreetView.pm:21
msgid ""
-"You are reporting the following update for being abusive, containing "
-"personal information, or similar:"
+"Map contains Ordnance Survey data &copy; Crown copyright and database right "
+"2010."
msgstr ""
-"Du rapporterer at følgende oppdatering er støtende, inneholder personlig "
-"informasjon, eller lignende:"
+"Kartet inneholder data fra Ordnance Survey &copy; Crown copyright og "
+"databaserettigheter 2010."
-#: web/contact.cgi:207
+#: perllib/FixMyStreet/Map/FMS.pm:27
msgid ""
-"You are reporting the following problem report for being abusive, containing "
-"personal information, or similar:"
+"Map contains Ordnance Survey data &copy; Crown copyright and database right "
+"2010. Microsoft"
msgstr ""
-"Du rapporterer at følgende problem er støtende, inneholder personlig "
-"informasjon eller lignende:"
+"Kartet inneholder data fra Ordnance Survey &copy; Crown copyright og "
+"databaserettigheter 2010. Microsoft"
-#: web/contact.cgi:234
-msgid "Your name:"
-msgstr "Ditt navn:"
+#: templates/web/default/contact/index.html:90
+msgid "Message:"
+msgstr "Melding:"
-#: web/contact.cgi:235
-msgid "Your&nbsp;email:"
-msgstr "Din&nbsp;epost:"
+#: templates/web/default/report/display.html:38
+msgid "More problems nearby"
+msgstr "Flere saker i nærheten"
-#: web/contact.cgi:236 web/index.cgi:765 web-admin/index.cgi:623
-msgid "Subject:"
-msgstr "Tema:"
+#: templates/web/default/my/my.html:1
+#, fuzzy
+msgid "My Reports"
+msgstr "Alle rapporter"
-#: web/contact.cgi:237
-msgid "Message:"
-msgstr "Melding:"
+#: templates/web/default/admin/list_updates.html:7
+#: templates/web/default/admin/search_reports.html:14
+#: templates/web/default/reports/index.html:10
+#: templates/web/emptyhomes/reports/index.html:10
+#: templates/web/fiksgatami/reports/index.html:9
+msgid "Name"
+msgstr "Navn"
-#: web/contact.cgi:238 web/index.cgi:1127
-msgid "Post"
-msgstr "Send inn"
+#: templates/web/default/admin/report_edit.html:27
+#: templates/web/default/admin/update_edit.html:23
+#: templates/web/default/report/display.html:81
+#: templates/web/default/report/new/fill_in_details.html:130
+msgid "Name:"
+msgstr "Navn:"
-#: web/faq.cgi:18
-msgid "Frequently Asked Questions"
-msgstr "Ofte spurte spørsmål"
+#: templates/web/default/footer.html:4 templates/web/fiksgatami/footer.html:3
+msgid "Navigation"
+msgstr "Navigasjon"
-#: web/flickr.cgi:45
-msgid ""
-"Thank you for trying to register for your Flickr photos. We seem to have a\n"
-"problem ourselves though, so <a href=\"/contact\">please let us know what "
-"went on</a>\n"
-"and we'll look into it.\n"
+#: templates/web/default/email_sent.html:24
+msgid "Nearly Done! Now check your email..."
msgstr ""
-"Takk for at du prøver å legge til dine Flickr bilder. Vi har et\n"
-"problem med siden, så <a href=\"/contact\"> vennligst meldt fra til Oss, </"
-"a>\n"
-"så skal vi se saken.\n"
-#: web/fun.cgi:17 web/fun.cgi:25
-msgid "Weird and Wonderful reports"
-msgstr "Merkelige saker"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:291
+msgid "New category contact added"
+msgstr "Ny kategorikontakt lagt til"
-#: web/index.cgi:82
-msgid "Submitting your report"
-msgstr "Legg til din sak/problem"
+#: db/alert_types.pl:18 db/alert_types.pl:22
+msgid "New local problems on FixMyStreet"
+msgstr "Nye lokale problemer på FiksGataMi"
-#: web/index.cgi:85
-msgid "Submitting your update"
-msgstr "Legg til din oppdatering"
+#: db/alert_types_eha.pl:12
+msgid "New local reports on reportemptyhomes.com"
+msgstr "Nye lokale rapporter på reportemptyhomes.com"
-#: web/index.cgi:89 web/index.cgi:669
-msgid "Reporting a problem"
-msgstr "Legger til en sak"
+#: templates/web/default/reports/council.html:43
+#: templates/web/default/reports/council.html:44
+#: templates/web/default/reports/index.html:11
+#: templates/web/emptyhomes/reports/index.html:11
+#: templates/web/fiksgatami/reports/index.html:10
+msgid "New problems"
+msgstr "Nye saker"
-#: web/index.cgi:92
-msgid "Viewing a problem"
-msgstr "Ser på en sak"
+#: db/alert_types.pl:38
+msgid "New problems for {{COUNCIL}} within {{WARD}} ward on FixMyStreet"
+msgstr "Nye problemer for {{COUNCIL}} innenfor {{WARD}} bydel på FiksGataMi"
-#: web/index.cgi:95
-msgid "Viewing a location"
-msgstr "Ser på et sted"
+#: db/alert_types.pl:26 db/alert_types.pl:30
+msgid "New problems near {{POSTCODE}} on FixMyStreet"
+msgstr "Nye problemer nær {{POSTCODE}} på FiksGataMi"
-#: web/index.cgi:120
-msgid "Enter a nearby GB postcode, or street name and area"
-msgstr "Skriv inn GB-postnummer i nærheten, eller veinavn og sted"
+#: db/alert_types.pl:10
+msgid "New problems on FixMyStreet"
+msgstr "Nye problemer på Fiksgatami"
-#: web/index.cgi:134
-msgid "Report, view, or discuss local problems"
-msgstr "Rapporter, finn eller diskuter lokale problemer"
+#: db/alert_types.pl:34
+msgid "New problems to {{COUNCIL}} on FixMyStreet"
+msgstr "Nye problemer i {{COUNCIL}} på FiksGataMi"
-#: web/index.cgi:135
-msgid "(like graffiti, fly tipping, broken paving slabs, or street lighting)"
-msgstr "(som tagging, søppel, hull i veien, eller manglende gatelys)"
+#: db/alert_types.pl:42
+#, fuzzy
+msgid "New problems within {{NAME}}'s boundary on FixMyStreet"
+msgstr "Nye problemer i {{COUNCIL}} på FiksGataMi"
-#: web/index.cgi:159
+#: db/alert_types_eha.pl:23
msgid ""
-"Thanks for uploading your photo. We now need to locate your problem, so "
-"please enter a nearby street name or postcode in the box below&nbsp;:"
+"New reports for {{COUNCIL}} within {{WARD}} ward on reportemptyhomes.com"
msgstr ""
-"Takk for at du lastet opp ditt bilde. Nå trenger vi å plassere ditt problem,"
-"så vær så snill å skriv inn navn på vei i nærheten, eller postnummer i "
-"boksen under&nbsp;:"
+"Nye rapporter for {{COUNCIL}} innenfor {{WARD}} bydel på reportemptyhomes."
+"com"
-#: web/index.cgi:177
-msgid "How to report a problem"
-msgstr "Hvordan rapportere et problem"
+#: db/alert_types_eha.pl:5
+msgid "New reports on reportemptyhomes.com"
+msgstr "Nye rapporter på reportemptyhomes.com"
-#: web/index.cgi:180
-msgid "Locate the problem on a map of the area"
-msgstr "Lokaliser problemet på kartet over området"
+#: db/alert_types_eha.pl:16
+msgid "New reports on reportemptyhomes.com near {{POSTCODE}}"
+msgstr "Nye rapporter på reportemptyhomes.com nær {{POSTCODE}}"
-#: web/index.cgi:181
-msgid "Enter details of the problem"
-msgstr "Legg inn detaljer om problemet"
+#: db/alert_types_eha.pl:19
+msgid "New reports to {{COUNCIL}} on reportemptyhomes.com"
+msgstr "Nye rapporter til {{COUNCIL}} på reportemptyhomes.com"
-#: web/index.cgi:182
-msgid "We send it to the council on your behalf"
-msgstr "Vi sender til administrasjon på dine vegne"
+#: db/alert_types_eha.pl:27
+msgid "New reports within {{NAME}}'s boundary on reportemptyhomes.com"
+msgstr "Nye rapporter innenfor grensen til {{NAME}} på reportemptyhomes.com"
-#: web/index.cgi:197
-msgid "Photos of recent reports"
-msgstr "Bilder av nylig meldte saker"
+#: templates/web/default/index.html:23
+msgid "New!"
+msgstr ""
+
+#: templates/web/default/admin/council_contacts.html:32
+#: templates/web/default/admin/council_contacts.html:33
+#: templates/web/default/admin/council_edit.html:4
+#: templates/web/default/admin/list_updates.html:28
+#: templates/web/default/admin/report_edit.html:19
+#: templates/web/default/admin/report_edit.html:37
+#: templates/web/default/admin/search_reports.html:43
+#: templates/web/default/admin/update_edit.html:16
+#: templates/web/default/questionnaire/creator_fixed.html:16
+#: templates/web/default/questionnaire/index.html:109
+#: templates/web/default/questionnaire/index.html:70
+msgid "No"
+msgstr "Nei"
-#: web/index.cgi:199
-msgid "Recently reported problems"
-msgstr "Nylig meldte problemer"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:187
+msgid "No council selected"
+msgstr "Ingen administrasjon er valgt"
-#: web/index.cgi:232
-msgid "Please enter a message"
-msgstr "Legg til en melding"
+#: templates/web/default/admin/council_list.html:32
+msgid "No edits have yet been made."
+msgstr "Ingenting er redigert."
-#: web/index.cgi:235 web/index.cgi:319
-msgid "Please enter your email"
-msgstr "Legg inn din epost"
+#: templates/web/default/admin/council_list.html:38
+msgid "No info at all"
+msgstr "Helt uten informasjon"
-#: web/index.cgi:237 web/index.cgi:321
-msgid "Please enter a valid email"
-msgstr "Legg til en gyldig epost"
+#: templates/web/default/around/around_map_list_items.html:15
+msgid "No problems found."
+msgstr "Ingen saker er funnet."
-#: web/index.cgi:246 web/index.cgi:394
-#, perl-format
-msgid ""
-"That image doesn't appear to have uploaded correctly (%s), please try again."
-msgstr "Bildet ser ikke ut til å blitt lastet opp riktig (%s), prøv på nytt."
+#: templates/web/default/around/on_map_list_items.html:12
+msgid "No problems have been reported yet."
+msgstr "Ingen saker er rapporter"
-#: web/index.cgi:268 web-admin/index.cgi:478
-msgid "Anonymous"
-msgstr "Anonym"
+#: templates/web/default/admin/council_list.html:5
+#: templates/web/default/admin/report_edit.html:16
+msgid "None"
+msgstr "Ingen"
-#: web/index.cgi:310
-msgid "No council selected"
-msgstr "Ingen administrasjon er valgt"
+#: templates/web/default/admin/questionnaire.html:6
+#, fuzzy
+msgid "Not reported before"
+msgstr "Rapportert tidligere"
-#: web/index.cgi:311
-msgid "Please enter a subject"
-msgstr "Legg inn et tema"
+#: templates/web/default/report/_main.html:9
+msgid "Not reported to council"
+msgstr "Ikke rapportert til administrasjonen"
-#: web/index.cgi:312
-msgid "Please enter some details"
-msgstr "Legg inn opplysninger om saken"
+#: templates/web/default/admin/council_contacts.html:24
+#: templates/web/default/admin/council_edit.html:46
+msgid "Note"
+msgstr "Merk"
-#: web/index.cgi:314
-msgid "Please enter your name"
-msgstr "Legg inn ditt navn"
+#: templates/web/default/admin/council_contacts.html:73
+#: templates/web/default/admin/council_edit.html:31
+#, fuzzy
+msgid "Note:"
+msgstr "Merk: "
-#: web/index.cgi:316
-msgid ""
-"Please enter your full name, councils need this information - if you do not "
-"wish your name to be shown on the site, untick the box"
-msgstr ""
-"Legg inn ditt fulle navn, administrasjoner som mottar saken trenger dette - "
-"hvis du ikke ønsker at ditt navn skal vises, kryss av her"
+#: templates/web/default/report/display.html:33
+#: templates/web/default/report/updates.html:19
+msgid "Offensive? Unsuitable? Tell us"
+msgstr "Støtende? Upassende? Gi oss beskjed"
-#: web/index.cgi:323 web/index.cgi:587 web/index.cgi:603
-msgid "-- Pick a category --"
-msgstr "-- velg en kategori --"
+#: templates/web/default/reports/council.html:72
+#: templates/web/default/reports/council.html:73
+msgid "Old fixed"
+msgstr "Eldre saker som er løst"
-#: web/index.cgi:324 web/index.cgi:363 web/index.cgi:370
-msgid "Please choose a category"
-msgstr "Velg en kategori"
+#: templates/web/default/reports/council.html:60
+#: templates/web/default/reports/council.html:61
+msgid "Old problems, state unknown"
+msgstr "Eldre saker med ukjent status"
-#: web/index.cgi:326 web/index.cgi:580
-msgid "-- Pick a property type --"
-msgstr "-- velg en eiendomsstype --"
+#: templates/web/default/reports/index.html:13
+#: templates/web/fiksgatami/reports/index.html:12
+msgid "Old problems,<br>state unknown"
+msgstr "Eldre saker, <br>ukjent status"
-#: web/index.cgi:327
-msgid "Please choose a property type"
-msgstr "Velg en type egenskap"
+#: templates/web/default/reports/index.html:15
+#: templates/web/emptyhomes/reports/index.html:14
+#: templates/web/fiksgatami/reports/index.html:14
+msgid "Older fixed"
+msgstr "Eldre løste"
-#: web/index.cgi:350
-msgid "That location is not part of that council"
-msgstr "Det stedet er ikke en del av den administrasjonen"
+#: templates/web/default/reports/council.html:50
+#: templates/web/default/reports/council.html:52
+#: templates/web/default/reports/council.html:56
+#: templates/web/default/reports/council.html:57
+#: templates/web/default/reports/index.html:12
+#: templates/web/emptyhomes/reports/index.html:12
+#: templates/web/fiksgatami/reports/index.html:11
+msgid "Older problems"
+msgstr "Eldre saker"
-#: web/index.cgi:374
-msgid "We have details for that council"
-msgstr "Vi har opplysninger om den administrasjonen"
+#: templates/web/default/admin/report_edit.html:22
+#: templates/web/default/admin/update_edit.html:19
+msgid "Open"
+msgstr "Ã…pen"
-#: web/index.cgi:383
-msgid "Somehow, you only have one co-ordinate. Please try again."
+#: templates/web/default/alert/list.html:88
+msgid "Or problems reported to:"
+msgstr "Eller problemer meldt til:"
+
+#: templates/web/default/alert/list.html:63
+msgid ""
+"Or you can subscribe to an alert based upon what ward or council you&rsquo;"
+"re in:"
msgstr ""
-"På en eller annen måte har du bare en koordinat, vennligst prøv på nytt."
+"Eller du kan abonnere på varsel basert på bydel eller administrasjon du "
+"hører inn under:"
-#: web/index.cgi:385
-msgid "You haven't specified any sort of co-ordinates. Please try again."
-msgstr "Du har ikke spesifisert koordinater, vennligst prøv på nytt"
+#: bin/send-reports:165 bin/send-reports:174
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:492
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:501
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:856
+#: perllib/FixMyStreet/DB/Result/Problem.pm:316
+#: perllib/FixMyStreet/DB/Result/Problem.pm:326
+#: perllib/FixMyStreet/DB/Result/Problem.pm:336
+#: perllib/FixMyStreet/DB/Result/Problem.pm:348
+msgid "Other"
+msgstr "Annet"
-#: web/index.cgi:424
-#, perl-format
-msgid ""
-"You have successfully confirmed your report and you can now <a href=\"%s"
-"\">view it on the site</a>."
+#: templates/web/default/errors/page_error_410_gone.html:1
+#: templates/web/default/errors/page_error_410_gone.html:3
+msgid "Page Gone"
msgstr ""
-"Du har nå lagt til en sak og kan <a href=\"%s\">se saken på nettsiden</a>."
-#: web/index.cgi:566
-msgid ""
-"That spot does not appear to be covered by a council.\n"
-"If you have tried to report an issue past the shoreline, for example,\n"
-"please specify the closest point on land."
+#: templates/web/default/errors/page_error_404_not_found.html:1
+#: templates/web/default/errors/page_error_404_not_found.html:3
+msgid "Page Not Found"
msgstr ""
-"Punktet ser ikke ut til å være dekket av en administrasjon.\n"
-"Hvis du har forsøkt å rapportere en sak utenfor kysten, for eksempel,\n"
-"så marker nærmeste punkt på land."
-#: web/index.cgi:580
-msgid "Empty house or bungalow"
-msgstr "Tomt hus eller bungalow"
+#: templates/web/default/admin/report_edit.html:22
+msgid "Partial"
+msgstr "Delvis"
-#: web/index.cgi:581
-msgid "Empty flat or maisonette"
+#: templates/web/default/auth/change_password.html:26
+#: templates/web/default/auth/general.html:33
+msgid "Password:"
msgstr ""
-#: web/index.cgi:581
-msgid "Whole block of empty flats"
-msgstr "Hel blokk med tomme leiligheter"
-
-#: web/index.cgi:582
-msgid "Empty office or other commercial"
-msgstr "Tomt kontor eller forretningsbygg"
+#: bin/send-reports:68 templates/web/default/admin/report_edit.html:29
+#: templates/web/default/report/new/fill_in_details.html:158
+msgid "Phone:"
+msgstr "Telefon:"
-#: web/index.cgi:582
-msgid "Empty pub or bar"
-msgstr "Tom pub eller bar"
+#: templates/web/default/questionnaire/index.html:97
+#: templates/web/default/report/display.html:125
+#: templates/web/default/report/new/fill_in_details.html:120
+msgid "Photo:"
+msgstr "Bilde:"
-#: web/index.cgi:583
-msgid "Empty public building - school, hospital, etc."
-msgstr "Tom offentlig bygning - skole, sykehos, etc."
+#: templates/web/default/alert/list.html:29
+msgid "Photos of recent nearby reports"
+msgstr "Bilder av nye saker i nærheten"
-#: web/index.cgi:584
-msgid "Property type:"
-msgstr "Type egenskap:"
+#: templates/web/default/index.html:78
+msgid "Photos of recent reports"
+msgstr "Bilder av nylig meldte saker"
-#: web/index.cgi:588 web/index.cgi:604 web-admin/index.cgi:629
-msgid "Category:"
-msgstr "Kategori:"
+#: templates/web/default/report/new/notes.html:6
+msgid "Please be polite, concise and to the point."
+msgstr "Vær høflig, poengtert og kortfattet."
-#: web/index.cgi:663
-msgid ""
-"Please note your report has <strong>not yet been sent</strong>. Choose a "
-"category and add further information below, then submit."
+#: templates/web/default/auth/change_password.html:12
+#: templates/web/default/auth/change_password.html:17
+msgid "Please check the passwords and try again"
msgstr ""
-#: web/index.cgi:666
-msgid ""
-"You have located the problem at the point marked with a purple pin on the "
-"map.\n"
-"If this is not the correct location, simply click on the map again. "
-msgstr ""
-"Du har plassert problemet ved punktet i kartet som er markert med en lilla "
-"nål. Hvis dette ikke er korrekt plassering kan du ganske enkelt klikke på "
-"kartet på nytt."
+#: templates/web/default/auth/token.html:17
+#, fuzzy
+msgid "Please check your email"
+msgstr "Skriv din epost"
-#: web/index.cgi:672 web/index.cgi:697 web/index.cgi:699 web/index.cgi:712
-msgid " or "
-msgstr " eller "
+#: templates/web/default/auth/general.html:14
+#: templates/web/default/auth/general.html:8
+#, fuzzy
+msgid "Please check your email address is correct"
+msgstr "Skriv en gyldig epostadresse"
-#: web/index.cgi:674
-#, perl-format
-msgid ""
-"All the information you provide here will be sent to <strong>%s</strong>.\n"
-"On the site, we will show the subject and details of the problem, plus your\n"
-"name if you give us permission."
-msgstr ""
-"All informasjonen du legger inn her vil bli sendt til <strong>%s</strong>.\n"
-"PÃ¥ dette nettestedet vil vi vise tema og detaljer om problemet, pluss ditt "
-"navn\n"
-"hvis du gir oss lov."
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:640
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:660
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:676
+#: perllib/FixMyStreet/DB/Result/Problem.pm:206
+msgid "Please choose a category"
+msgstr "Velg en kategori"
-#: web/index.cgi:678
-#, fuzzy, perl-format
+#: perllib/FixMyStreet/DB/Result/Problem.pm:212
+msgid "Please choose a property type"
+msgstr "Velg en type egenskap"
+
+#: templates/web/default/contact/blurb.html:2
msgid ""
-"All the information you\n"
-" provide here will be sent to <strong>%s</strong> or a relevant\n"
-" local body such as TfL, via the London Report-It system. The\n"
-" subject and details of the problem will be public, plus your "
-"name\n"
-" if you give us permission."
+"Please do <strong>not</strong> report problems through this form; messages "
+"go to\n"
+"the team behind FixMyStreet, not a council. To report a problem,\n"
+"please <a href=\"/\">go to the front page</a> and follow the instructions."
msgstr ""
-"All informasjonen du gir oss her vil bli sendt til <strong>%s</strong>.\n"
-"Tittelen og detaljene for problemet vil bli offentlig, pluss ditt navn\n"
-"hvis du gir oss tillatelse."
+"Vennligst <strong>ikke</strong> rapporter feil gjennom denne siden; "
+"meldinger går til\n"
+"gruppen som står bak fiksgatami, og ikke til en administrasjon. For å "
+"rapportere et problem,\n"
+"vennligst <a href=\"/\">gå til forsiden</a> og følg instruksjonene."
-#: web/index.cgi:684
-#, perl-format
+#: templates/web/default/report/new/notes.html:7
msgid ""
-"All the information you provide here will be sent to <strong>%s</strong>.\n"
-"The subject and details of the problem will be public, plus your\n"
-"name if you give us permission."
+"Please do not be abusive &mdash; abusing your council devalues the service "
+"for all users."
msgstr ""
-"All informasjonen du gir oss her vil bli sendt til <strong>%s</strong>.\n"
-"Tittelen og detaljene for problemet vil bli offentlig, pluss ditt navn\n"
-"hvis du gir oss tillatelse."
+"Ikke vær ufin &mdash; å kjefte på din administrasjon skader verdien av "
+"tjenesten for alle brukerne."
-#: web/index.cgi:698
-msgid "All the information you provide here will be sent to"
-msgstr "All informasjonen du har lagt inn her vil bli sendt til"
+#: perllib/FixMyStreet/DB/Result/Comment.pm:108
+msgid "Please enter a message"
+msgstr "Legg til en melding"
-#: web/index.cgi:701
-msgid ""
-"The subject and details of the problem will be public, plus your name if you "
-"give us permission."
-msgstr ""
-"Tittelen og detaljene for problemet vil bli offentlig, pluss navnet ditt\n"
-"hvis du gir oss tillatelse til det."
+#: templates/web/default/auth/change_password.html:12
+#: templates/web/default/auth/change_password.html:15
+#, fuzzy
+msgid "Please enter a password"
+msgstr "Legg til en melding"
-#: web/index.cgi:703
-msgid ""
-"We do <strong>not</strong> yet have details for the other council that "
-"covers this location."
-msgid_plural ""
-"We do <strong>not</strong> yet have details for the other councils that "
-"cover this location."
-msgstr[0] ""
-"Vi har ennå <strong>ikke</strong> detaljene for den andre administrasjonen "
-"som dekker dette stedet."
-msgstr[1] ""
-"Vi har ennå <strong>ikke</strong> detaljene for de andre administrasjonene "
-"som dekker dette stedet."
+#: perllib/FixMyStreet/App/Controller/Contact.pm:95
+#: perllib/FixMyStreet/DB/Result/Problem.pm:181
+msgid "Please enter a subject"
+msgstr "Legg inn et tema"
-#: web/index.cgi:707 web/index.cgi:722
-#, perl-format
-msgid ""
-"You can help us by finding a contact email address for local problems for %s "
-"and emailing it to us at <a href='mailto:%s'>%s</a>."
-msgstr ""
-"Du kan hjelpe oss ved å finne en kontakt-epostadresse for lokale problemer i "
-"%s, og sende den via epost til oss på <a href='mailto:%s'>%s</a>."
+#: perllib/FixMyStreet/DB/Result/User.pm:91
+msgid "Please enter a valid email"
+msgstr "Legg til en gyldig epost"
-#: web/index.cgi:717 web/index.cgi:725
-msgid "We do not yet have details for the council that covers this location."
-msgid_plural ""
-"We do not yet have details for the councils that cover this location."
-msgstr[0] ""
-"Vi har ennå ikke detaljer for administrasjonen som dekker dette stedet."
-msgstr[1] ""
-"Vi har ennå ikke detaljer for administrasjonene som dekker dette stedet."
+#: perllib/FixMyStreet/App/Controller/Alert.pm:123
+#: perllib/FixMyStreet/App/Controller/Contact.pm:105
+msgid "Please enter a valid email address"
+msgstr "Legg inn din epost"
-#: web/index.cgi:721
-msgid ""
-"If you submit a problem here the subject and details of the problem will be "
-"public, but the problem will <strong>not</strong> be reported to the council."
-msgstr ""
-"Hvis du sender inn et problem hit, så vil tema og detaljer for problemet "
-"være offentlig, men problemet vil <strong>ikke</strong> bli rapportert til "
-"administrasjonen."
+#: perllib/FixMyStreet/DB/Result/Problem.pm:184
+msgid "Please enter some details"
+msgstr "Legg inn opplysninger om saken"
-#: web/index.cgi:727
-msgid ""
-"If you submit a report here it will be left on the site, but not reported to "
-"the council &ndash; please still leave your report, so that we can show to "
-"the council the activity in their area."
-msgstr ""
-"Hvis du sender inn en rapport her så vil den bli tilgjengelig her men ikke "
-"rapportert til administrasjonen. &ndash; det er fint om du likevel sender "
-"inn din rapport, slik at vi kan vise administrasjonen aktiviteten i deres "
-"område."
+#: perllib/FixMyStreet/App/Controller/Contact.pm:94
+#: perllib/FixMyStreet/DB/Result/User.pm:88
+#: templates/web/default/auth/general.html:13
+#: templates/web/default/auth/general.html:8
+msgid "Please enter your email"
+msgstr "Legg inn din epost"
-#: web/index.cgi:733
+#: perllib/FixMyStreet/DB/Result/Problem.pm:199
+#: perllib/FixMyStreet/DB/Result/User.pm:83
msgid ""
-"Please fill in the form below with details of the problem,\n"
-"and describe the location as precisely as possible in the details box."
+"Please enter your full name, councils need this information - if you do not "
+"wish your name to be shown on the site, untick the box"
msgstr ""
-"Vennligst fyll inn skjemaet under med detaljene om problemet,\n"
-"og beskriv stedet så nøyaktig som mulig i boksen for detaljer."
+"Legg inn ditt fulle navn, administrasjoner som mottar saken trenger dette - "
+"hvis du ikke ønsker at ditt navn skal vises, kryss av her"
-#: web/index.cgi:742
+#: perllib/FixMyStreet/App/Controller/Contact.pm:93
+#: perllib/FixMyStreet/DB/Result/Problem.pm:192
+#: perllib/FixMyStreet/DB/Result/User.pm:76
+msgid "Please enter your name"
+msgstr "Legg inn ditt navn"
+
+#: templates/web/emptyhomes/report/new/fill_in_details_text.html:1
msgid ""
"Please fill in details of the empty property below, saying what type of\n"
"property it is e.g. an empty home, block of flats, office etc. Tell us\n"
@@ -1310,10 +1367,16 @@ msgid ""
"concise\n"
"and to the point; writing your message entirely in block capitals makes it "
"hard\n"
-"to read, as does a lack of punctuation.\n"
+"to read, as does a lack of punctuation."
msgstr ""
-#: web/index.cgi:750
+#: templates/web/default/report/new/fill_in_details_text.html:1
+#: templates/web/default/report/new/fill_in_details_text.html:8
+msgid "Please fill in details of the problem below."
+msgstr "Vennligst fyll ut detaljer om saken under"
+
+#: templates/web/default/report/new/fill_in_details_text.html:1
+#: templates/web/default/report/new/fill_in_details_text.html:3
msgid ""
"Please fill in details of the problem below. The council won't be able\n"
"to help unless you leave as much detail as you can, so please describe the "
@@ -1329,80 +1392,272 @@ msgstr ""
"det har\n"
"vært der, en beskrivelse (og et bilde av problemet hvis du har et), etc."
-#: web/index.cgi:755
-msgid "Please fill in details of the problem below."
-msgstr "Vennligst fyll ut detaljer om saken under"
+#: templates/web/default/report/new/fill_in_details.html:49
+#, fuzzy
+msgid ""
+"Please fill in the form below with details of the problem, and describe the "
+"location as precisely as possible in the details box."
+msgstr ""
+"Vennligst fyll inn skjemaet under med detaljene om problemet,\n"
+"og beskriv stedet så nøyaktig som mulig i boksen for detaljer."
-#: web/index.cgi:764
-msgid "Empty property details form"
-msgstr "Tom eiendom detaljskjema"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:239
+msgid "Please indicate whether you'd like to receive another questionnaire"
+msgstr "Vennligst indiker om du ønsker å motta et annet spørreskjema"
-#: web/index.cgi:766 web-admin/index.cgi:624
-msgid "Details:"
-msgstr "Detaljer:"
+#: templates/web/default/report/display.html:69
+msgid ""
+"Please note that updates are not sent to the council. If you leave your name "
+"it will be public. Your information will only be used in accordance with our "
+"<a href=\"/faq#privacy\">privacy policy</a>"
+msgstr ""
+"Vennligst merk at oppdateringer ikke blir sendt til administrasjonen. Hvis "
+"du legger igjen navnet ditt så vil det være offentlig tilgjengelig. Din "
+"informasjon vil kun bli brukt i henhold til våre <a href=\"/faq#privacy"
+"\">personvernpolicy</a>"
-#: web/index.cgi:767 web/index.cgi:1143 web/questionnaire.cgi:308
-msgid "Photo:"
-msgstr "Bilde:"
+#: templates/web/default/report/new/fill_in_details.html:34
+msgid ""
+"Please note your report has <strong>not yet been sent</strong>. Choose a "
+"category and add further information below, then submit."
+msgstr ""
-#: web/index.cgi:768 web/index.cgi:1124 web-admin/index.cgi:630
-#: web-admin/index.cgi:750
-msgid "Name:"
-msgstr "Navn:"
+#: templates/web/default/report/new/notes.html:1
+msgid "Please note:"
+msgstr "Vennligst merk deg:"
-#: web/index.cgi:770 bin/send-reports:94 web-admin/index.cgi:632
-msgid "Phone:"
-msgstr "Telefon:"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:242
+msgid "Please provide some explanation as to why you're reopening this report"
+msgstr ""
+"Vennligst bidra med en forklaring for hvorfor du åpner denne rapporten på "
+"nytt"
-#: web/index.cgi:771 web/index.cgi:1131
-msgid "(optional)"
-msgstr "(valgfritt)"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:249
+msgid "Please provide some text as well as a photo"
+msgstr "Vennligst bidra med litt tekst i tilegg til et bilde"
-#: web/index.cgi:773
-msgid "Can we show your name on the site?"
-msgstr "Kan vi vise ditt navn her på nettstedet?"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:119
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:235
+msgid ""
+"Please say whether you've ever reported a problem to your council before"
+msgstr ""
+"Gi oss informasjon om du har rapportert en sak til din administrasjon "
+"tidligere"
-#: web/index.cgi:775
-msgid "Can we show your name publicly?"
-msgstr "Kan vi vise ditt navn offentlig?"
+#: perllib/FixMyStreet/App/Controller/Alert.pm:85
+msgid "Please select the feed you want"
+msgstr "Velg den feed du ønsker"
-#: web/index.cgi:777
-msgid "(we never show your email address or phone number)"
-msgstr "(vi viser aldri din epostadresse eller telefonnummer)"
+#: perllib/FixMyStreet/App/Controller/Alert.pm:125
+msgid "Please select the type of alert you want"
+msgstr "Velg vilken type varsel du ønsker"
-#: web/index.cgi:804
-msgid "Please note:"
-msgstr "Vennligst merk deg:"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:231
+msgid "Please state whether or not the problem has been fixed"
+msgstr "Vennligs oppgi om dette problemet er blitt fikset eller ikke"
-#: web/index.cgi:806
-msgid ""
-"We will only use your personal information in accordance with our <a href=\"/"
-"faq#privacy\">privacy policy.</a>"
-msgstr ""
-"Vi vil kun bruke personlig informasjon om deg i henhold til vår <a href=\"/"
-"faq#privacy\">personvernpolicy.</a>"
+#: templates/web/default/questionnaire/index.html:52
+msgid "Please take a look at the updates that have been left."
+msgstr "Vær sa snill å ta en titt på oppdateringene som har blitt igjen."
-#: web/index.cgi:807
-msgid "Please be polite, concise and to the point."
-msgstr "Vær høflig, poengtert og kortfattet."
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:729
+msgid "Please upload a JPEG image only"
+msgstr "Vennligst last kun opp et JPEG-bilde"
-#: web/index.cgi:808
-msgid ""
-"Please do not be abusive &mdash; abusing your council devalues the service "
-"for all users."
-msgstr ""
-"Ikke vær ufin &mdash; å kjefte på din administrasjon skader verdien av "
-"tjenesten for alle brukerne."
+#: perllib/FixMyStreet/App/Controller/Contact.pm:96
+msgid "Please write a message"
+msgstr "Skriv inn en melding"
-#: web/index.cgi:809
-msgid ""
-"Writing your message entirely in block capitals makes it hard to read, as "
-"does a lack of punctuation."
+#: templates/web/default/contact/index.html:93
+#: templates/web/default/report/display.html:138
+msgid "Post"
+msgstr "Send inn"
+
+#: templates/web/default/report/updates.html:8
+msgid "Posted anonymously at %s"
+msgstr "Publisert anonymt %s"
+
+#: templates/web/default/report/updates.html:10
+msgid "Posted by %s at %s"
+msgstr "Sendt inn av %s %s"
+
+#: perllib/FixMyStreet/Map/Tilma/Original.pm:90
+#: templates/web/default/maps/openlayers.html:81
+#: templates/web/default/maps/tilma/original.html:62
+msgid "Problem"
+msgstr "Problem"
+
+#: templates/web/default/admin/timeline.html:24
+msgid "Problem %d created"
+msgstr "Problem %d opprettet"
+
+#: templates/web/default/admin/timeline.html:26
+msgid "Problem %s confirmed"
+msgstr "Problem %s bekreftet"
+
+#: templates/web/default/admin/timeline.html:28
+msgid "Problem %s sent to council %s"
+msgstr "Problem %s sendt til administrasjon %s"
+
+#: templates/web/default/admin/index.html:27
+msgid "Problem breakdown by state"
+msgstr "Tilstandsfordeling for problemer"
+
+#: perllib/FixMyStreet/App/Controller/Admin.pm:745
+msgid "Problem marked as open."
+msgstr "Problem markert som åpent."
+
+#: templates/web/default/around/display_location.html:73
+msgid "Problems in this area"
+msgstr "Saker i dette område"
+
+#: db/alert_types.pl:14
+msgid "Problems recently reported fixed on FixMyStreet"
+msgstr "Problemer nylig rapportert fikset på FiksGataMi"
+
+#: templates/web/default/alert/list.html:52
+#, fuzzy
+msgid "Problems within %.1fkm of this location"
+msgstr "Saker innen %skm av dette punktet"
+
+#: perllib/FixMyStreet/Cobrand/Default.pm:694
+#: perllib/FixMyStreet/Cobrand/EmptyHomes.pm:161
+#: perllib/FixMyStreet/Cobrand/FiksGataMi.pm:123
+msgid "Problems within %s"
+msgstr "Problemer innenfor %s"
+
+#: perllib/FixMyStreet/Cobrand/Default.pm:703
+#: perllib/FixMyStreet/Cobrand/EmptyHomes.pm:169
+msgid "Problems within %s ward"
+msgstr "Problemer innenfor %s avdeling/etat"
+
+#: templates/web/default/reports/council.html:11
+#: templates/web/default/reports/council.html:14
+msgid "Problems within %s, FixMyStreet"
+msgstr "Problemer innenfor %s, Fiksgatami"
+
+#: templates/web/default/alert/list.html:69
+msgid "Problems within the boundary of:"
+msgstr "Problemer innenfor grensene av:"
+
+#: db/alert_types_eha.pl:8
+msgid "Properties recently reported as put back to use on reportemptyhomes.com"
msgstr ""
-"Når du skriver meldingen din med kun store bokstaver blir den vanskelig å "
-"lese. Det samme gjelder manglende tegnsetting."
-#: web/index.cgi:810
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:469
+msgid "Property type:"
+msgstr "Type egenskap:"
+
+#: templates/web/default/report/display.html:64
+msgid "Provide an update"
+msgstr "Bidra med oppdatering"
+
+#: templates/web/default/questionnaire/index.html:0
+#: templates/web/default/questionnaire/index.html:14
+#: templates/web/default/questionnaire/index.html:4
+msgid "Questionnaire"
+msgstr "Spørreskjema"
+
+#: templates/web/default/admin/timeline.html:32
+msgid "Questionnaire %d answered for problem %d, %s to %s"
+msgstr "Spørreskjema %d fylt inn for problem %d, %s til %s"
+
+#: templates/web/default/admin/timeline.html:30
+msgid "Questionnaire %d sent for problem %d"
+msgstr "Spørreskjema %d sendt for problem %d"
+
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:185
+msgid "Questionnaire filled in by problem reporter"
+msgstr "Spørreskjema fylt inn av feilrapportøren"
+
+#: templates/web/default/alert/list.html:54
+#: templates/web/default/around/display_location.html:1
+#: templates/web/default/around/display_location.html:3
+#: templates/web/default/report/display.html:55
+#: templates/web/default/reports/council.html:17
+msgid "RSS feed"
+msgstr "RSS-strøm"
+
+#: perllib/FixMyStreet/Cobrand/Default.pm:732
+#: perllib/FixMyStreet/Cobrand/Default.pm:746
+#, fuzzy
+msgid "RSS feed for %s"
+msgstr "RSS-strøm fra %s"
+
+#: perllib/FixMyStreet/Cobrand/Default.pm:739
+#: perllib/FixMyStreet/Cobrand/Default.pm:753
+#, fuzzy
+msgid "RSS feed for %s ward, %s"
+msgstr "RSS-strøm fra %s"
+
+#: perllib/FixMyStreet/Cobrand/Default.pm:762
+#: perllib/FixMyStreet/Cobrand/Default.pm:776
+#: perllib/FixMyStreet/Cobrand/FiksGataMi.pm:139
+#: perllib/FixMyStreet/Cobrand/FiksGataMi.pm:147
+#: perllib/FixMyStreet/Cobrand/FiksGataMi.pm:157
+#: perllib/FixMyStreet/Cobrand/FiksGataMi.pm:165
+msgid "RSS feed of %s"
+msgstr "RSS-strøm fra %s"
+
+#: perllib/FixMyStreet/Cobrand/Default.pm:768
+#: perllib/FixMyStreet/Cobrand/Default.pm:782
+#, fuzzy
+msgid "RSS feed of %s, within %s ward"
+msgstr "RSS-strøm for problemer i denne %s"
+
+#: templates/web/default/alert/list.html:54
+msgid "RSS feed of nearby problems"
+msgstr "RSS-strøm med problemer i nærheten"
+
+#: templates/web/default/reports/council.html:17
+msgid "RSS feed of problems in this %s"
+msgstr "RSS-strøm for problemer i denne %s"
+
+#: perllib/FixMyStreet/Cobrand/Default.pm:695
+#: perllib/FixMyStreet/Cobrand/EmptyHomes.pm:162
+#: perllib/FixMyStreet/Cobrand/FiksGataMi.pm:122
+#, fuzzy
+msgid "RSS feed of problems within %s"
+msgstr "RSS-strøm for problemer i denne %s"
+
+#: perllib/FixMyStreet/Cobrand/Default.pm:702
+#: perllib/FixMyStreet/Cobrand/EmptyHomes.pm:168
+#, fuzzy
+msgid "RSS feed of problems within %s ward"
+msgstr "RSS-strøm for problemer i denne %s"
+
+#: templates/web/default/around/display_location.html:1
+#: templates/web/default/around/display_location.html:4
+msgid "RSS feed of recent local problems"
+msgstr "RSS-strøm med nylige lokale problemer"
+
+#: templates/web/default/report/display.html:55
+msgid "RSS feed of updates to this problem"
+msgstr "RSS-strøm med oppdateringer for dette problemet"
+
+#: templates/web/default/alert/updates.html:9
+#: templates/web/default/report/display.html:45
+msgid "Receive email when updates are left on this problem."
+msgstr "Motta epost når det er uppdateringer på denne saken"
+
+#: templates/web/default/around/display_location.html:0
+#: templates/web/default/around/display_location.html:35
+msgid "Recent local problems, FixMyStreet"
+msgstr "Nylig lokalt problem, FiksGataMi."
+
+#: templates/web/default/reports/council.html:68
+#: templates/web/default/reports/council.html:69
+#: templates/web/default/reports/index.html:14
+#: templates/web/emptyhomes/reports/index.html:13
+#: templates/web/fiksgatami/reports/index.html:13
+msgid "Recently fixed"
+msgstr "Nylig løste saker"
+
+#: templates/web/default/index.html:87
+msgid "Recently reported problems"
+msgstr "Nylig meldte problemer"
+
+#: templates/web/default/report/new/notes.html:9
msgid ""
"Remember that FixMyStreet is primarily for reporting physical problems that "
"can be fixed. If your problem is not appropriate for submission via this "
@@ -1414,459 +1669,482 @@ msgstr ""
"tjenesten, husk at du kan kontakte administrasjonen direkte via deres egen "
"nettside."
-#: web/index.cgi:812
-msgid ""
-"FixMyStreet and the Guardian are providing this service in partnership in <a "
-"href=\"/faq#privacy\">certain cities</a>. In those cities, both have access "
-"to any information submitted, including names and email addresses, and will "
-"use it only to ensure the smooth running of the service, in accordance with "
-"their privacy policies."
-msgstr ""
+#: templates/web/default/admin/report_edit.html:43
+#: templates/web/default/admin/update_edit.html:33
+msgid "Remove photo (can't be undone!)"
+msgstr "Fjern bilde (kan ikke gjøres om!)"
-#: web/index.cgi:916
-msgid "Hide stale reports"
-msgstr "Gjem utdaterte rapporter"
+#: perllib/FixMyStreet/Cobrand/EmptyHomes.pm:77
+msgid "Report Empty Homes"
+msgstr "Rapporter tomme hjem"
-#: web/index.cgi:919
-msgid "Include stale reports"
-msgstr "Inkludert utdaterte rapporter"
+#: templates/web/default/footer.html:6 templates/web/emptyhomes/header.html:30
+#: templates/web/fiksgatami/footer.html:5
+msgid "Report a problem"
+msgstr "Rapporter et problem"
-#: web/index.cgi:938
-msgid "No problems have been reported yet."
-msgstr "Ingen saker er rapporter"
+#: perllib/FixMyStreet/App/Controller/Rss.pm:262
+msgid "Report on %s"
+msgstr "Rapport på %s"
-#: web/index.cgi:954
-msgid "No problems found."
-msgstr "Ingen saker er funnet."
+#: templates/web/default/index.html:11
+msgid "Report, view, or discuss local problems"
+msgstr "Rapporter, finn eller diskuter lokale problemer"
-#: web/index.cgi:959
-msgid "Show pins"
-msgstr "Vis nåler"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:331
+#: templates/web/default/contact/index.html:45
+msgid "Reported anonymously at %s"
+msgstr "Rapportert anonymt %s"
-#: web/index.cgi:963
-msgid "Hide pins"
-msgstr "Gjem nåler"
+#: templates/web/default/admin/questionnaire.html:5
+#: templates/web/default/questionnaire/index.html:79
+msgid "Reported before"
+msgstr "Rapportert tidligere"
-#: web/index.cgi:1004
-msgid "Email me new local problems"
-msgstr "Send meg epost om lokale problemer"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:323
+msgid "Reported by %s anonymously at %s"
+msgstr "Publisert av %s anonymt %s"
-#: web/index.cgi:1006
-msgid "RSS feed of recent local problems"
-msgstr "RSS-strøm med nylige lokale problemer"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:354
+#: templates/web/default/contact/index.html:47
+msgid "Reported by %s at %s"
+msgstr "Publisert av %s %s"
-#: web/index.cgi:1009
-msgid "Problems in this area"
-msgstr "Saker i dette område"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:345
+msgid "Reported by %s by %s at %s"
+msgstr "Rapporter av %s av %s %s"
+
+#: perllib/FixMyStreet/DB/Result/Problem.pm:319
+msgid "Reported by %s in the %s category anonymously at %s"
+msgstr "Rapportert av %s i kategorien %s anonnymt %s"
-#: web/index.cgi:1010
+#: perllib/FixMyStreet/DB/Result/Problem.pm:339
+msgid "Reported by %s in the %s category by %s at %s"
+msgstr "Rapportert av %s i kategorien %s av %s %s"
+
+#: perllib/FixMyStreet/DB/Result/Problem.pm:327
+msgid "Reported in the %s category anonymously at %s"
+msgstr "Rapportert i kategorien %s anonymt %s"
+
+#: perllib/FixMyStreet/DB/Result/Problem.pm:349
+msgid "Reported in the %s category by %s at %s"
+msgstr "Rapportert i kategorien %s av %s %s"
+
+#: templates/web/default/around/around_index.html:1
+#: templates/web/default/report/new/fill_in_details.html:0
+#: templates/web/default/report/new/fill_in_details.html:3
+#: templates/web/default/report/new/fill_in_details.html:30
+msgid "Reporting a problem"
+msgstr "Legger til en sak"
+
+#: templates/web/default/around/display_location.html:103
msgid "Reports on and around the map"
msgstr "Saker i og rundt kartet"
-#: web/index.cgi:1011
-#, perl-format
-msgid "Closest nearby problems <small>(within&nbsp;%skm)</small>"
-msgstr "De nærmeste sakene <small>(innenfor&nbsp;%skm)</small>"
+#: templates/web/default/admin/report_edit.html:32
+msgid "Resend report"
+msgstr "Send rapport på nytt"
+
+#: templates/web/default/admin/council_edit.html:35
+msgid "Save changes"
+msgstr "Lagre endringer"
+
+#: perllib/FixMyStreet/App/Controller/Admin.pm:599
+#: templates/web/default/admin/search_reports.html:1
+msgid "Search Reports"
+msgstr "Søk i rapporter"
-#: web/index.cgi:1015
+#: templates/web/default/admin/search_reports.html:5
+msgid "Search:"
+msgstr "Søk:"
+
+#: templates/web/default/alert/list.html:41
+#, fuzzy
msgid ""
-"To report a problem, simply\n"
-" <strong>click on the map</strong> at the correct location."
+"Select which type of alert you'd like and click the button for an RSS feed, "
+"or enter your email address to subscribe to an email alert."
msgstr ""
-"For å lage en sak, \n"
-" <strong>klikk på kartet</strong> på riktig sted."
+"Velg hvilken type varsel du ønsker og klikk på knappen for en RSS-strøm, "
+"eller skriv inn din epostadresse for å abonnere på et epostvarsel."
-#: web/index.cgi:1017
-#, perl-format
+#: perllib/FixMyStreet/DB/Result/Problem.pm:378
+msgid "Sent to %s %s later"
+msgstr "Sendt til %s %s senere"
+
+#: templates/web/default/admin/report_edit.html:32
+msgid "Sent:"
+msgstr "Sendt:"
+
+#: templates/web/default/admin/report_edit.html:34
+msgid "Service:"
+msgstr "Tjeneste:"
+
+#: templates/web/emptyhomes/static/about.html:21
+msgid "Shelter Cymru"
+msgstr ""
+
+#: templates/web/emptyhomes/static/about.html:23
msgid ""
-"<small>If you cannot see the map, <a href='%s' rel='nofollow'>skip this\n"
-" step</a>.</small>"
+"Shelter Cymru is Wales&rsquo; people and homes charity and wants\n"
+" everyone in Wales to have a decent home. We believe a home is a "
+"fundamental\n"
+" right and essential to the health and well-being of people and "
+"communities.\n"
+" We work for people in housing need. We have offices all over Wales and\n"
+" prevent people from losing their homes by offering free, confidential "
+"and\n"
+" independent advice. When necessary we constructively challenge on behalf "
+"of\n"
+" people to ensure they are properly assisted and to improve practice and\n"
+" learning. We believe that bringing empty homes back into use can make a\n"
+" significant contribution to the supply of affordable homes in Wales."
msgstr ""
-"<small>Hvis du ikke kan se kartet, <a href='%s' rel='nofollow'>hopp over "
-"dette\n"
-" steget</a>.</small>"
-#: web/index.cgi:1022
-msgid "Recent local problems, FixMyStreet"
-msgstr "Nylig lokalt problem, FiksGataMi."
+#: templates/web/default/around/display_location.html:52
+msgid "Show pins"
+msgstr "Vis nåler"
-#: web/index.cgi:1035
-msgid "There were problems with your update. Please see below."
-msgstr "Det var problemer med din oppdatering. Vennligst se under."
+#: templates/web/default/alert/index.html:31
+msgid "Some photos of recent reports"
+msgstr "Noen bilder av nylig meldte saker"
-#: web/index.cgi:1056 web/index.cgi:1058
-msgid "Unknown problem ID"
-msgstr "Ukjent problem-Id"
+#: perllib/FixMyStreet/App/View/Email.pm:32
+#: perllib/FixMyStreet/App/View/Web.pm:38
+msgid "Some text to localize"
+msgstr ""
-#: web/index.cgi:1059
-msgid "That report has been removed from FixMyStreet."
-msgstr "Den rapporten har blitt fjernet fra FiksGataMi."
+#: templates/web/default/admin/council_list.html:42
+msgid "Some unconfirmeds"
+msgstr "Noen ubekreftede"
-#: web/index.cgi:1079
-msgid "This problem is old and of unknown status."
-msgstr "Denne saken er gammel og med ukjent status."
+#: perllib/FixMyStreet/Geocode.pm:171
+msgid ""
+"Sorry, that appears to be a Crown dependency postcode, which we don't cover."
+msgstr ""
+"Beklager det ser ut til å være et \"Crown dependency\"-postnummer, som vi "
+"ikke dekker."
-#: web/index.cgi:1082 web/index.cgi:1123
-msgid "This problem has been fixed"
-msgstr "Denne saken er løst"
+#: perllib/FixMyStreet/Geocode.pm:71
+msgid ""
+"Sorry, that location appears to be too general; please be more specific."
+msgstr ""
+"Beklager, den plasserigen ser ut til å være for generell. Forsøk å være mer "
+"spesifikk."
-#: web/index.cgi:1107
-msgid "More problems nearby"
-msgstr "Flere saker i nærheten"
+#: templates/web/default/tokens/abuse.html:5
+msgid "Sorry, there has been an error confirming your problem."
+msgstr "Beklager, det har oppstått problemer når vi under lagringen av saken"
-#: web/index.cgi:1110
-msgid "Email me updates"
-msgstr "Send meg oppdateringer"
+#: perllib/FixMyStreet/Geocode.pm:103 perllib/FixMyStreet/Geocode.pm:150
+msgid "Sorry, we could not find that location."
+msgstr "Beklager, vi kunne ikke finne det stedet."
-#: web/index.cgi:1113
-msgid "Receive email when updates are left on this problem"
-msgstr "Motta epost når oppdateringer legges inn om dette problemet"
+#: perllib/FixMyStreet/Geocode.pm:148
+msgid "Sorry, we could not parse that location. Please try again."
+msgstr "Beklager, vi kunne ikke tolke den posisjonen. Vennligst prøv på nytt."
-#: web/index.cgi:1118
-msgid "RSS feed of updates to this problem"
-msgstr "RSS-strøm med oppdateringer for dette problemet"
+#: templates/web/default/admin/list_updates.html:6
+#: templates/web/default/admin/search_reports.html:21
+msgid "State"
+msgstr "Tilstand"
-#: web/index.cgi:1119
-msgid "Provide an update"
-msgstr "Bidra med oppdatering"
+#: templates/web/default/admin/report_edit.html:21
+#: templates/web/default/admin/update_edit.html:18
+msgid "State:"
+msgstr "Tilstand:"
-#: web/index.cgi:1125
-msgid "Update:"
-msgstr "Oppdatering:"
+#: templates/web/default/admin/report_edit.html:13
+#: templates/web/default/contact/index.html:83
+#: templates/web/default/report/new/fill_in_details.html:91
+msgid "Subject:"
+msgstr "Tema:"
-#: web/index.cgi:1126
-msgid "Alert me to future updates"
-msgstr "Send meg varsel ved fremtidige oppdateringer"
+#: templates/web/default/questionnaire/creator_fixed.html:19
+#: templates/web/default/report/new/fill_in_details.html:171
+msgid "Submit"
+msgstr "Send inn"
-#: web/index.cgi:1134
-msgid ""
-"Please note that updates are not sent to the council. If you leave your name "
-"it will be public. Your information will only be used in accordance with our "
-"<a href=\"/faq#privacy\">privacy policy</a>"
-msgstr ""
-"Vennligst merk at oppdateringer ikke blir sendt til administrasjonen. Hvis "
-"du legger igjen navnet ditt så vil det være offentlig tilgjengelig. Din "
-"informasjon vil kun bli brukt i henhold til våre <a href=\"/faq#privacy"
-"\">personvernpolicy</a>"
+#: templates/web/default/admin/report_edit.html:46
+#: templates/web/default/admin/update_edit.html:36
+msgid "Submit changes"
+msgstr "Send inn endringer"
-#: web/index.cgi:1154
-msgid "Updates to this problem, FixMyStreet"
-msgstr "Oppdateringer til dette problemet, FiksGataMi"
+#: templates/web/default/questionnaire/index.html:114
+msgid "Submit questionnaire"
+msgstr "Lever spørreskjema"
-#: web/questionnaire.cgi:29 web/questionnaire.cgi:80 web/questionnaire.cgi:213
-#: web/questionnaire.cgi:233
-msgid "Questionnaire"
-msgstr "Spørreskjema"
+#: templates/web/default/alert/updates.html:17
+#: templates/web/default/report/display.html:50
+msgid "Subscribe"
+msgstr "Abonner"
-#: web/questionnaire.cgi:41
-msgid ""
-"I'm afraid we couldn't validate that token. If you've copied the URL from an "
-"email, please check that you copied it exactly.\n"
-msgstr ""
-"Jeg er redd vi ikke kunne verifisere den referansen. Hvis du kopierte den "
-"URLen fra en annen epost, sjekk at du har kopiert den korrekt.\n"
+#: templates/web/default/alert/list.html:127
+msgid "Subscribe me to an email alert"
+msgstr "Jeg ønsker å abonnere på epostvarsel"
-#: web/questionnaire.cgi:48
-#, perl-format
-msgid ""
-"You have already answered this questionnaire. If you have a question, please "
-"<a href='%s'>get in touch</a>, or <a href='%s'>view your problem</a>.\n"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:597
+#: templates/web/default/admin/index.html:1
+msgid "Summary"
+msgstr "Oppsummering"
+
+#: templates/web/default/reports/index.html:1
+#: templates/web/emptyhomes/reports/index.html:1
+#: templates/web/fiksgatami/reports/index.html:1
+msgid "Summary reports"
+msgstr "Oppsummeringsrapporter"
+
+#: perllib/FixMyStreet/App/Controller/Admin.pm:601
+#: templates/web/default/admin/questionnaire.html:1
+msgid "Survey Results"
msgstr ""
-"Du har allerede besvart dette spørreskjemaet. Hvis du har spørsmål, "
-"vennligst <a href='%s'>ta kontakt</a>, eller <a href='%s'>se på ditt "
-"problem</a>.\n"
-#: web/questionnaire.cgi:53
-msgid "I'm afraid we couldn't locate your problem in the database.\n"
-msgstr "Jeg er redd vi ikke klarte å finne ditt problem i databasen.\n"
+#: templates/web/default/admin/list_updates.html:12
+msgid "Text"
+msgstr "Tekst"
-#: web/questionnaire.cgi:92
-msgid "Please state whether or not the problem has been fixed"
-msgstr "Vennligs oppgi om dette problemet er blitt fikset eller ikke"
+#: templates/web/default/admin/council_contacts.html:12
+msgid "Text only version"
+msgstr "Tekst-versjon"
+
+#: templates/web/default/admin/update_edit.html:13
+msgid "Text:"
+msgstr "Tekst:"
-#: web/questionnaire.cgi:95
+#: templates/web/default/tokens/confirm_update.html:7
+#: templates/web/default/tokens/confirm_update.html:8
msgid ""
-"Please say whether you've ever reported a problem to your council before"
+"Thank you &mdash; you can <a href=\"%s\">view your updated problem</a> on "
+"the site."
msgstr ""
-"Gi oss informasjon om du har rapportert en sak til din administrasjon "
-"tidligere"
-
-#: web/questionnaire.cgi:97
-msgid "Please indicate whether you'd like to receive another questionnaire"
-msgstr "Vennligst indiker om du ønsker å motta et annet spørreskjema"
+"Tusen takk &mdash; du kan <a href=\"%s\">se på ditt oppdaterte problem</a> "
+"her hos oss."
-#: web/questionnaire.cgi:99
-msgid "Please provide some explanation as to why you're reopening this report"
+#: templates/web/emptyhomes/tokens/confirm_problem.html:6
+#: templates/web/emptyhomes/tokens/confirm_problem.html:8
+msgid ""
+"Thank you for reporting an empty property on ReportEmptyHomes.com. We have "
+"emailed the lead officer for empty homes in the council responsible with "
+"details, and asked them to do whatever they can to get the empty property "
+"back into use as soon as possible."
msgstr ""
-"Vennligst bidra med en forklaring for hvorfor du åpner denne rapporten på "
-"nytt"
-
-#: web/questionnaire.cgi:115
-msgid "Please provide some text as well as a photo"
-msgstr "Vennligst bidra med litt tekst i tilegg til et bilde"
-
-#: web/questionnaire.cgi:146
-msgid "Questionnaire filled in by problem reporter"
-msgstr "Spørreskjema fylt inn av feilrapportøren"
-#: web/questionnaire.cgi:166
+#: templates/web/emptyhomes/tokens/confirm_problem.html:30
+#: templates/web/emptyhomes/tokens/confirm_problem.html:31
msgid ""
-"<p>Thank you very much for filling in our questionnaire; if you\n"
-"get some more information about the status of your problem, please come back "
-"to the\n"
-"site and leave an update.</p>\n"
+"Thank you for reporting this empty property on ReportEmptyHomes.com.\n"
+"At present the report cannot be sent through to the council for this area. "
+"We\n"
+"are working with councils to link them into the system so that as many "
+"areas\n"
+"as possible will be covered."
msgstr ""
-"<p>Tusen takk for at du fylte ut vårt spørreskjema. Hvis du\n"
-"får mer informasjon om status for ditt problem, vær så snill å kom tilbake "
-"hit til\n"
-"nettstedet og legg igjen en oppdatering.</p>\n"
-#: web/questionnaire.cgi:174
-#, perl-format
+#: templates/web/default/tokens/error.html:7
+#, fuzzy
msgid ""
-"<p style=\"font-size:150%%\">We're sorry to hear that. We have two "
-"suggestions: why not try\n"
-"<a href=\"%s\">writing direct to your councillor(s)</a>\n"
-"or, if it's a problem that could be fixed by local people working together,\n"
-"why not <a href=\"http://www.pledgebank.com/new\">make and publicise a "
-"pledge</a>?\n"
-"</p>\n"
+"Thank you for trying to confirm your update or problem. We seem to have an "
+"error ourselves though, so <a href=\"%s\">please let us know what went on</"
+"a> and we'll look into it."
msgstr ""
-"<p style=\"font-size:150%%\">Det gjør oss triste å høre dette. Vi har to "
-"forslag: hva med å forsøke\n"
-"<a href=\"%s\">Ã¥ skrive direkte til dine representanter</a>, eller hvis det "
-"er et problem som kan fikses\n"
-"av naboer som jobber sammen, hva med å <a href=\"http://www.pledgebank.com/"
-"new\">publisere en utfordring om å bidra</a>?</p>\n"
+"Takk for at du forsøker å bekrefte din oppdatering eller ditt problem. Vi "
+"ser ut til å ha en\n"
+"feil hos oss, så <a href=\"%s\">vær så snill å fortell oss hva som skjedde</"
+"a>\n"
+"så skal vi se på saken.\n"
-#: web/questionnaire.cgi:183
+#: templates/web/emptyhomes/tokens/confirm_problem.html:24
+#: templates/web/emptyhomes/tokens/confirm_problem.html:26
msgid ""
-"<p style=\"font-size:150%\">Thank you very much for filling in our "
-"questionnaire; glad to hear it's been fixed.</p>\n"
+"Thank you for using ReportEmptyHomes.com. Your action is already helping to "
+"resolve the UK&rsquo;s empty homes crisis."
msgstr ""
-"<p style=\"font-size:150%\">Tusen takk for at du fylte ut vårt "
-"spørreskjema.\n"
-"Veldig glad for å høre at det er blitt fikset.</p>\n"
-#: web/questionnaire.cgi:236
-msgid "Don&rsquo;t know"
-msgstr "Vet ikke"
+#: templates/web/default/around/around_index.html:44
+msgid ""
+"Thanks for uploading your photo. We now need to locate your problem, so "
+"please enter a nearby street name or postcode in the box below&nbsp;:"
+msgstr ""
+"Takk for at du lastet opp ditt bilde. Nå trenger vi å plassere ditt problem,"
+"så vær så snill å skriv inn navn på vei i nærheten, eller postnummer i "
+"boksen under&nbsp;:"
-#: web/questionnaire.cgi:237
-msgid "Submit questionnaire"
-msgstr "Lever spørreskjema"
+#: templates/web/default/contact/submit.html:8
+msgid "Thanks for your feedback. We'll get back to you as soon as we can!"
+msgstr ""
+"Takk for ditt innspill. Vi gir deg en tilbakemelding så raskt som mulig"
-#: web/questionnaire.cgi:254
+#: templates/web/default/questionnaire/creator_fixed.html:9
msgid ""
-"<p>Getting empty homes back into use can be difficult. You shouldn't expect\n"
-"the property to be back into use yet. But a good council will have started "
-"work\n"
-"and should have reported what they have done on the website. If you are not\n"
-"satisfied with progress or information from the council, now is the right "
-"time\n"
-"to say. You may also want to try contacting some other people who may be "
-"able\n"
-"to help. For advice on how to do this and other useful information please\n"
-"go to <a href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://"
-"www.emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
+"Thanks, glad to hear it's been fixed! Could we just ask if you have ever "
+"reported a problem to a council before?"
msgstr ""
+"Takk, glad for å høre at problemet er fikset! Vi vil gjerne spørre deg om "
+"du har rapportert et problem til en administrasjon tidligere?"
-#: web/questionnaire.cgi:264
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:738
msgid ""
-"<p>Getting empty homes back into use can be difficult, but by now a good "
-"council\n"
-"will have made a lot of progress and reported what they have done on the\n"
-"website. Even so properties can remain empty for many months if the owner "
-"is\n"
-"unwilling or the property is in very poor repair. If nothing has happened "
-"or\n"
-"you are not satisfied with the progress the council is making, now is the "
-"right\n"
-"time to say so. We think it's a good idea to contact some other people who\n"
-"may be able to help or put pressure on the council For advice on how to do\n"
-"this and other useful information please go to <a\n"
-"href=\"http://www.emptyhomes.com/getinvolved/campaign.html\">http://www."
-"emptyhomes.com/getinvolved/campaign.html</a>.</p>\n"
-msgstr ""
+"That image doesn't appear to have uploaded correctly (%s), please try again."
+msgstr "Bildet ser ikke ut til å blitt lastet opp riktig (%s), prøv på nytt."
-#: web/questionnaire.cgi:277
+#: templates/web/default/alert/index.html:12
msgid ""
-"The details of your problem are available on the right hand side of this "
-"page."
+"That location does not appear to be covered by a council, perhaps it is "
+"offshore - please try somewhere more specific."
msgstr ""
-"Detaljene om ditt problem er tilgjengelig på høyre side av denne siden."
-
-#: web/questionnaire.cgi:278
-msgid "Please take a look at the updates that have been left."
-msgstr "Vær sa snill å ta en titt på oppdateringene som har blitt igjen."
+"Det stedet dekkes ikke av noen administrasjon, kanskje det er til havs - "
+"vennligst forsøk et mer spesifikt sted."
-#: web/questionnaire.cgi:284
-msgid "An update marked this problem as fixed."
-msgstr "En oppdatering markerte dette problemet som fikset."
+#: perllib/FixMyStreet/Geocode.pm:81
+msgid "That location does not appear to be in Britain; please try again."
+msgstr "Det stedet virker ikke å være i storbritannia. Vennligst prøv igjen."
-#: web/questionnaire.cgi:285
-msgid "Has this problem been fixed?"
-msgstr "Har denne saken blitt løst?"
+#: perllib/FixMyStreet/Geocode.pm:165
+msgid "That postcode was not recognised, sorry."
+msgstr "Det postnummeret ble ikke gjenkjent, beklager."
-#: web/questionnaire.cgi:292 web-admin/index.cgi:889
-msgid "Reported before"
-msgstr "Rapportert tidligere"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:498
+msgid "That problem will now be resent."
+msgstr "Det problemet vil nå bli sendt på nytt."
-#: web/questionnaire.cgi:293
-msgid "First time"
-msgstr "Første gang"
+#: perllib/FixMyStreet/App/Controller/Report.pm:75
+#: perllib/FixMyStreet/App/Controller/Rss.pm:41
+msgid "That report has been removed from FixMyStreet."
+msgstr "Den rapporten har blitt fjernet fra FiksGataMi."
-#: web/questionnaire.cgi:294
+#: templates/web/default/around/around_index.html:27
+#, fuzzy
msgid ""
-"Have you ever reported a problem to a council before, or is this your first "
-"time?"
+"That spot does not appear to be covered by a council. If you have tried to "
+"report an issue past the shoreline, for example, please specify the closest "
+"point on land."
msgstr ""
-"Har du sendt en sak til en administrasjon før, eller er dette første gangen?"
+"Punktet ser ikke ut til å være dekket av en administrasjon.\n"
+"Hvis du har forsøkt å rapportere en sak utenfor kysten, for eksempel,\n"
+"så marker nærmeste punkt på land."
-#: web/questionnaire.cgi:304
-msgid ""
-"If you wish to leave a public update on the problem, please enter it here\n"
-"(please note it will not be sent to the council). For example, what was\n"
-"your experience of getting the problem fixed?"
-msgstr ""
-"Hvis du ønsker å legge til en offentlig kommentar til saken, legg den til "
-"her\n"
-"(denne blir sendt til administrasjonen). Du kan for eksempel\n"
-"dele din erfaring med hvordan din sak ble løst."
+#: templates/web/emptyhomes/static/about.html:7
+#, fuzzy
+msgid "The Empty Homes Agency"
+msgstr "Rapporter tomme hjem"
-#: web/questionnaire.cgi:322
+#: templates/web/emptyhomes/static/about.html:9
msgid ""
-"Would you like to receive another questionnaire in 4 weeks, reminding you to "
-"check the status?"
+"The Empty Homes agency is an independent campaigning charity. We\n"
+" are not part of government, and have no formal links with local "
+"councils\n"
+" although we work in cooperation with both. We exist to highlight the "
+"waste\n"
+" of empty property and work with others to devise and promote "
+"sustainable\n"
+" solutions to bring empty property back into use. We are based in London "
+"but\n"
+" work across England. We also work in partnership with other charities "
+"across\n"
+" the UK."
msgstr ""
-"Kunne du tenke deg å motta en ny forespørsel om 4 uker, som minner deg om å "
-"sjekke status?"
-#: web/reports.cgi:192 web/reports.cgi:239
-msgid "Summary reports"
-msgstr "Oppsummeringsrapporter"
-
-#: web/reports.cgi:194
+#: templates/web/default/email_sent.html:26
msgid ""
-"This is a summary of all reports on this site; select a particular council "
-"to see the reports sent there."
+"The confirmation email <strong>may</strong> take a few minutes to arrive "
+"&mdash; <em>please</em> be patient."
msgstr ""
-"Dette er en opplisting av alle sakene i denne tjenesten; velg en bestemt "
-"administrasjon for å se saker som er sendt dit."
-#: web/reports.cgi:195
-msgid "Greyed-out lines are councils that no longer exist."
+#: templates/web/default/questionnaire/index.html:51
+msgid ""
+"The details of your problem are available on the right hand side of this "
+"page."
msgstr ""
-"Linjer med grå bakgrunn er administrasjoner som ikke lenger eksisterer."
-
-#: web/reports.cgi:199 web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "Name"
-msgstr "Navn"
-
-#: web/reports.cgi:199 web/reports.cgi:275
-msgid "New problems"
-msgstr "Nye saker"
-
-#: web/reports.cgi:199 web/reports.cgi:283
-msgid "Older problems"
-msgstr "Eldre saker"
-
-#: web/reports.cgi:201
-msgid "Old problems,<br>state unknown"
-msgstr "Eldre saker, <br>ukjent status"
+"Detaljene om ditt problem er tilgjengelig på høyre side av denne siden."
-#: web/reports.cgi:203 web/reports.cgi:290
-msgid "Recently fixed"
-msgstr "Nylig løste saker"
+#: db/alert_types.pl:19 db/alert_types.pl:23 db/alert_types.pl:27
+#: db/alert_types.pl:31
+msgid "The latest local problems reported by users"
+msgstr "De siste lokale problemer rapporter av brukere"
-#: web/reports.cgi:203
-msgid "Older fixed"
-msgstr "Eldre løste"
+#: db/alert_types_eha.pl:13
+msgid "The latest local reports reported by users"
+msgstr "De siste lokale rapporter rapporter av brukere"
-#: web/reports.cgi:245
-msgid "council"
-msgstr "administrasjon"
+#: db/alert_types.pl:35
+msgid "The latest problems for {{COUNCIL}} reported by users"
+msgstr "De siste problemer for {{COUNCIL}} rapportert av brukere"
-#: web/reports.cgi:255
-#, perl-format
-msgid "RSS feed of problems in this %s"
-msgstr "RSS-strøm for problemer i denne %s"
+#: db/alert_types.pl:39
+msgid ""
+"The latest problems for {{COUNCIL}} within {{WARD}} ward reported by users"
+msgstr ""
+"De siste problemene for {{COUNCIL}} innenfor {{WARD}} bydel rapportert av "
+"brukere"
-#: web/reports.cgi:259
-#, perl-format
-msgid "This is a summary of all reports for one %s."
-msgstr "Dette er en oppsummering av alle rapporter for en %s."
+#: db/alert_types.pl:11
+msgid "The latest problems reported by users"
+msgstr "De siste problemene rapportert av brukere"
-#: web/reports.cgi:260
-#, perl-format
-msgid "This is a summary of all reports for this %s."
-msgstr "Dette er en oppsummering for alle rapporter for denne %s."
+#: db/alert_types.pl:15
+msgid "The latest problems reported fixed by users"
+msgstr "De siste problemer rapporter fikset av brukere"
-#: web/reports.cgi:264
-#, perl-format
-msgid "You can <a href=\"%s\">see less detail</a>."
-msgstr "Du kan <a href=\"%s\">se mindre detaljer</a>."
+#: db/alert_types.pl:43
+#, fuzzy
+msgid "The latest problems within {{NAME}}'s boundary reported by users"
+msgstr "De siste rapporter innenfor grensen til {{NAME}} rapportert av brukere"
-#: web/reports.cgi:266
-#, perl-format
-msgid "You can <a href=\"%s\">see more details</a>."
-msgstr "Du kan <a href=\"%s\">se flere detaljer</a>."
+#: db/alert_types_eha.pl:9
+msgid "The latest properties reported back to use by users"
+msgstr "De siste eiendommer rapportert tilbake i bruk av brukere"
-#: web/reports.cgi:268
-#, perl-format
-msgid ""
-"You can <a href=\"%s\">see less detail</a> or go back and <a href=\"/reports"
-"\">show all councils</a>."
-msgstr ""
-"Du kan <a href=\"%s\">se mindre detaljer</a> eller gå tilbake og <a href=\"/"
-"reports\">se alle administrasjonene</a>."
+#: db/alert_types_eha.pl:20
+msgid "The latest reports for {{COUNCIL}} reported by users"
+msgstr "De siste rapporter for {{COUNCIL}} rapportert av brukere"
-#: web/reports.cgi:270
-#, perl-format
+#: db/alert_types_eha.pl:24
msgid ""
-"You can <a href=\"%s\">see more details</a> or go back and <a href=\"/reports"
-"\">show all councils</a>."
+"The latest reports for {{COUNCIL}} within {{WARD}} ward reported by users"
msgstr ""
-"Du kan <a href=\"%s\">se flere detaljer</a> eller gå tilbake og <a href=\"/"
-"reports\">se alle administrasjonene</a>."
+"De siste rapporter for {{COUNCIL}} innenfor {{WARD}} bydel rapporter av "
+"brukere"
-#: web/reports.cgi:285
-msgid "Old problems, state unknown"
-msgstr "Eldre saker med ukjent status"
+#: db/alert_types_eha.pl:28
+msgid "The latest reports within {{NAME}}'s boundary reported by users"
+msgstr "De siste rapporter innenfor grensen til {{NAME}} rapportert av brukere"
-#: web/reports.cgi:291
-msgid "Old fixed"
-msgstr "Eldre saker som er løst"
+#: templates/web/default/auth/change_password.html:12
+#: templates/web/default/auth/change_password.html:16
+msgid "The passwords do not match"
+msgstr ""
-#: web/reports.cgi:294
-#, perl-format
-msgid "%s - Summary reports"
-msgstr "%s - oppsummeringsrapporter"
+#: templates/web/default/errors/page_error_410_gone.html:10
+#: templates/web/default/errors/page_error_410_gone.html:12
+msgid "The requested URL '%s' is no longer available"
+msgstr ""
-#: web/reports.cgi:294
-#, perl-format
-msgid "Problems within %s, FixMyStreet"
-msgstr "Problemer innenfor %s, Fiksgatami"
+#: templates/web/default/errors/page_error_404_not_found.html:10
+#: templates/web/default/errors/page_error_404_not_found.html:12
+msgid "The requested URL '%s' was not found on this server"
+msgstr ""
-#: web/reports.cgi:333
-msgid "(sent to both)"
-msgstr "(sent til begge)"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:479
+#: perllib/FixMyStreet/App/Controller/Admin.pm:649
+#: perllib/FixMyStreet/App/Controller/Admin.pm:688
+#: perllib/FixMyStreet/App/Controller/Admin.pm:773
+msgid "The requested URL was not found on this server."
+msgstr ""
-#: web/reports.cgi:334
-msgid "(not sent to council)"
-msgstr "(ikke rapportert til administrasjonen)"
+#: templates/web/default/alert/list.html:47
+msgid "The simplest alert is our geographic one:"
+msgstr "Den enkleste meldingen er vår geografiske:"
-#: bin/send-reports:96
+#: templates/web/default/report/new/all_councils_text.html:18
+#: templates/web/default/report/new/some_councils_text.html:10
+#: templates/web/default/report/new/some_councils_text.html:11
msgid ""
-"This web page also contains a photo of the problem, provided by the user."
+"The subject and details of the problem will be public, plus your name if you "
+"give us permission."
msgstr ""
-"Denne nettsiden inneholder også et bilde av problemet, sendt inn av brukeren."
-
-#: bin/send-reports:102
-msgid "To view a map of the precise location of this issue"
-msgstr "For å se en kart med den presise plasseringen for denne saken."
+"Tittelen og detaljene for problemet vil bli offentlig, pluss navnet ditt\n"
+"hvis du gir oss tillatelse til det."
-#: bin/send-reports:103
+#: bin/send-reports:77
msgid ""
"The user could not locate the problem on a map, but to see the area around "
"the location they entered"
@@ -1874,20 +2152,15 @@ msgstr ""
"Brukeren kunne ikke plassere problemet på et kart, men sjekk området rundt "
"stedet de skrev inn"
-#: bin/send-reports:187
-msgid "this type of local problem"
-msgstr "denne type lokalt problem"
-
-#: bin/send-reports:191
-#, perl-format
-msgid "Category: %s"
-msgstr "Kategori: %s"
+#: perllib/FixMyStreet/App/Controller/Contact.pm:115
+msgid "There were problems with your report. Please see below."
+msgstr "Det var problemer med din rapport. Vennligst se under."
-#: bin/send-reports:194
-msgid " and "
-msgstr " og "
+#: perllib/FixMyStreet/App/Controller/Report/Update.pm:202
+msgid "There were problems with your update. Please see below."
+msgstr "Det var problemer med din oppdatering. Vennligst se under."
-#: bin/send-reports:196
+#: bin/send-reports:175
msgid ""
"This email has been sent to both councils covering the location of the "
"problem, as the user did not categorise it; please ignore it if you're not "
@@ -1900,7 +2173,7 @@ msgstr ""
"eller gi oss beskjed om hvilken kategori av problemer dette er så vi kan "
"legge det til i vårt system."
-#: bin/send-reports:199
+#: bin/send-reports:178
#, fuzzy
msgid ""
"This email has been sent to several councils covering the location of the "
@@ -1913,1544 +2186,672 @@ msgstr ""
"eller gi oss beskjed om hvilken kategori av problemer dette er så vi kan "
"legge det til i vårt system."
-#: bin/send-reports:206
-#, perl-format
+#: templates/web/default/debug_header.html:3
msgid ""
-"We realise this problem might be the responsibility of %s; however, we don't "
-"currently have any contact details for them. If you know of an appropriate "
-"contact address, please do get in touch."
-msgstr ""
-"Vi innser at %s kan være ansvarlig for dette problemet, men vi mangler for "
-"tiden kontaktinformasjon for dem. Hvis du vet om en egnet kontaktadresse, ta "
-"kontakt med oss."
-
-#: db/alert_types_eha.pl:5
-msgid "New reports on reportemptyhomes.com"
-msgstr "Nye rapporter på reportemptyhomes.com"
-
-#: db/alert_types_eha.pl:8
-msgid "Properties recently reported as put back to use on reportemptyhomes.com"
+"This is a developer site; things might break at any time, and the database "
+"will be periodically deleted."
msgstr ""
+"Dette er et utviklernettsted. Ting kan knekke når som helst og databasen vil "
+"bli periodisk slettet."
-#: db/alert_types_eha.pl:9
-msgid "The latest properties reported back to use by users"
-msgstr "De siste eiendommer rapportert tilbake i bruk av brukere"
-
-#: db/alert_types_eha.pl:12
-msgid "New local reports on reportemptyhomes.com"
-msgstr "Nye lokale rapporter på reportemptyhomes.com"
-
-#: db/alert_types_eha.pl:13
-msgid "The latest local reports reported by users"
-msgstr "De siste lokale rapporter rapporter av brukere"
-
-#: db/alert_types_eha.pl:16
-#, perl-brace-format
-msgid "New reports on reportemptyhomes.com near {{POSTCODE}}"
-msgstr "Nye rapporter på reportemptyhomes.com nær {{POSTCODE}}"
-
-#: db/alert_types_eha.pl:19
-#, perl-brace-format
-msgid "New reports to {{COUNCIL}} on reportemptyhomes.com"
-msgstr "Nye rapporter til {{COUNCIL}} på reportemptyhomes.com"
-
-#: db/alert_types_eha.pl:20
-#, perl-brace-format
-msgid "The latest reports for {{COUNCIL}} reported by users"
-msgstr "De siste rapporter for {{COUNCIL}} rapportert av brukere"
+#: templates/web/default/reports/council.html:20
+msgid "This is a summary of all reports for one %s."
+msgstr "Dette er en oppsummering av alle rapporter for en %s."
-#: db/alert_types_eha.pl:23
-#, perl-brace-format
-msgid ""
-"New reports for {{COUNCIL}} within {{WARD}} ward on reportemptyhomes.com"
-msgstr ""
-"Nye rapporter for {{COUNCIL}} innenfor {{WARD}} bydel på reportemptyhomes."
-"com"
+#: templates/web/default/reports/council.html:22
+msgid "This is a summary of all reports for this %s."
+msgstr "Dette er en oppsummering for alle rapporter for denne %s."
-#: db/alert_types_eha.pl:24
-#, perl-brace-format
+#: templates/web/default/reports/index.html:4
+#: templates/web/emptyhomes/reports/index.html:4
+#: templates/web/fiksgatami/reports/index.html:4
msgid ""
-"The latest reports for {{COUNCIL}} within {{WARD}} ward reported by users"
+"This is a summary of all reports on this site; select a particular council "
+"to see the reports sent there."
msgstr ""
-"De siste rapporter for {{COUNCIL}} innenfor {{WARD}} bydel rapporter av "
-"brukere"
-
-#: db/alert_types_eha.pl:27
-#, perl-brace-format
-msgid "New reports within {{NAME}}'s boundary on reportemptyhomes.com"
-msgstr "Nye rapporter innenfor grensen til {{NAME}} på reportemptyhomes.com"
-
-#: db/alert_types_eha.pl:28
-#, perl-brace-format
-msgid "The latest reports within {{NAME}}'s boundary reported by users"
-msgstr "De siste rapporter innenfor grensen til {{NAME}} rapportert av brukere"
-
-#: db/alert_types.pl:5 db/alert_types.pl:6
-#, perl-brace-format
-msgid "Updates on {{title}}"
-msgstr "Oppdateringer av {{title}}"
-
-#: db/alert_types.pl:7
-#, perl-brace-format
-msgid "Update by {{name}}"
-msgstr "Oppdatert av {{name}}"
-
-#: db/alert_types.pl:10
-msgid "New problems on FixMyStreet"
-msgstr "Nye problemer på Fiksgatami"
-
-#: db/alert_types.pl:11
-msgid "The latest problems reported by users"
-msgstr "De siste problemene rapportert av brukere"
-
-#: db/alert_types.pl:14
-msgid "Problems recently reported fixed on FixMyStreet"
-msgstr "Problemer nylig rapportert fikset på FiksGataMi"
-
-#: db/alert_types.pl:15
-msgid "The latest problems reported fixed by users"
-msgstr "De siste problemer rapporter fikset av brukere"
-
-#: db/alert_types.pl:18 db/alert_types.pl:22
-msgid "New local problems on FixMyStreet"
-msgstr "Nye lokale problemer på FiksGataMi"
-
-#: db/alert_types.pl:19 db/alert_types.pl:23 db/alert_types.pl:27
-#: db/alert_types.pl:31
-msgid "The latest local problems reported by users"
-msgstr "De siste lokale problemer rapporter av brukere"
-
-#: db/alert_types.pl:26
-#, perl-brace-format
-msgid "New problems near {{POSTCODE}} on FixMyStreet"
-msgstr "Nye problemer nær {{POSTCODE}} på FiksGataMi"
+"Dette er en opplisting av alle sakene i denne tjenesten; velg en bestemt "
+"administrasjon for å se saker som er sendt dit."
-#: db/alert_types.pl:30
-#, perl-brace-format
-msgid "New problems NEAR {{POSTCODE}} on FixMyStreet"
-msgstr "Nye problemer nær {{POSTCODE}} på FiksGataMi"
+#: perllib/FixMyStreet/Cobrand/Default.pm:815
+#: perllib/FixMyStreet/Cobrand/EmptyHomes.pm:117
+#: templates/web/default/report/display.html:111
+msgid "This problem has been fixed"
+msgstr "Denne saken er løst"
-#: db/alert_types.pl:34
-#, perl-brace-format
-msgid "New problems to {{COUNCIL}} on FixMyStreet"
-msgstr "Nye problemer i {{COUNCIL}} på FiksGataMi"
+#: perllib/FixMyStreet/Cobrand/Default.pm:811
+msgid "This problem is old and of unknown status."
+msgstr "Denne saken er gammel og med ukjent status."
-#: db/alert_types.pl:35
-#, perl-brace-format
-msgid "The latest problems for {{COUNCIL}} reported by users"
-msgstr "De siste problemer for {{COUNCIL}} rapportert av brukere"
+#: perllib/FixMyStreet/DB/ResultSet/AlertType.pm:80
+msgid "This report is currently marked as fixed."
+msgstr "Denne rapporten er for tiden markert som fikset."
-#: db/alert_types.pl:38
-#, perl-brace-format
-msgid "New problems for {{COUNCIL}} within {{WARD}} ward on FixMyStreet"
-msgstr "Nye problemer for {{COUNCIL}} innenfor {{WARD}} bydel på FiksGataMi"
+#: perllib/FixMyStreet/DB/ResultSet/AlertType.pm:81
+msgid "This report is currently marked as open."
+msgstr "Denne rapporten er for tiden markert som åpen."
-#: db/alert_types.pl:39
-#, perl-brace-format
+#: bin/send-reports:70
msgid ""
-"The latest problems for {{COUNCIL}} within {{WARD}} ward reported by users"
+"This web page also contains a photo of the problem, provided by the user."
msgstr ""
-"De siste problemene for {{COUNCIL}} innenfor {{WARD}} bydel rapportert av "
-"brukere"
-
-#: db/alert_types.pl:42
-#, perl-brace-format
-msgid "New problems within {{NAME}}"
-msgstr "Nye problemer innenfor {{NAME}}"
-
-#: db/alert_types.pl:43
-#, perl-brace-format
-msgid "The latest problems within {{NAME}}"
-msgstr "De siste problemer innenfor {{NAME}}"
-
-#: web-admin/index.cgi:68 web-admin/index.cgi:124 web-admin/index.cgi:125
-msgid "Summary"
-msgstr "Oppsummering"
-
-#: web-admin/index.cgi:69 web-admin/index.cgi:183 web-admin/index.cgi:184
-msgid "Council contacts"
-msgstr "Administrasjonskontakter"
-
-#: web-admin/index.cgi:70 web-admin/index.cgi:465
-msgid "Search Reports"
-msgstr "Søk i rapporter"
+"Denne nettsiden inneholder også et bilde av problemet, sendt inn av brukeren."
-#: web-admin/index.cgi:71 web-admin/index.cgi:784 web-admin/index.cgi:785
+#: perllib/FixMyStreet/App/Controller/Admin.pm:600
+#: templates/web/default/admin/timeline.html:1
msgid "Timeline"
msgstr "Tidslinje"
-#: web-admin/index.cgi:72 web-admin/index.cgi:871 web-admin/index.cgi:872
-msgid "Survey Results"
+#: templates/web/default/admin/search_reports.html:13
+msgid "Title"
+msgstr "Tittel"
+
+#: templates/web/default/alert/index.html:21
+msgid ""
+"To find out what local alerts we have for you, please enter your GB\n"
+"postcode or street name and area:"
msgstr ""
+"Du finner lokale saker ved å søke på ditt postnummer, veinavn eller sted:"
-#: web-admin/index.cgi:85
-msgid "FixMyStreet administration"
-msgstr "Fiksgatami-administrasjon"
+#: templates/web/default/around/display_location.html:92
+#, fuzzy
+msgid ""
+"To report a problem, simply <strong>click on the map</strong> at the correct "
+"location."
+msgstr ""
+"For å lage en sak, \n"
+" <strong>klikk på kartet</strong> på riktig sted."
-#: web-admin/index.cgi:101
-msgid "FixMyStreet admin:"
-msgstr "FiksGataMi-admin:"
+#: bin/send-reports:76
+msgid "To view a map of the precise location of this issue"
+msgstr "For å se en kart med den presise plasseringen for denne saken."
-#: web-admin/index.cgi:154
-#, perl-format
-msgid "<strong>%d</strong> live problems"
-msgstr "<strong>%d</strong> aktive problemer"
+#: templates/web/default/maps/tilma/original.html:10
+msgid "Unable to fetch the map tiles from the tile server."
+msgstr "Klarte ikke hente kartfliser fra filtjeneren."
-#: web-admin/index.cgi:155
-#, perl-format
-msgid "%d live updates"
-msgstr "%d aktive oppdateringer"
+#: templates/web/default/admin/report_edit.html:22
+#: templates/web/default/admin/update_edit.html:19
+msgid "Unconfirmed"
+msgstr "Ubekreftet"
-#: web-admin/index.cgi:156
-#, perl-format
-msgid "%d confirmed alerts, %d unconfirmed"
-msgstr "%d bekreftede varsler, %d ubekreftede"
+#: perllib/FixMyStreet/App/Controller/Rss.pm:166
+#, fuzzy
+msgid "Unknown alert type"
+msgstr "Ukjent problem-Id"
-#: web-admin/index.cgi:157
-#, fuzzy, perl-format
-msgid "%d questionnaires sent &ndash; %d answered (%s%%)"
-msgstr "%d spørreskjema sendt &ndash; %d besvart (%d%%)"
+#: perllib/FixMyStreet/App/Controller/Report.pm:70
+#: perllib/FixMyStreet/App/Controller/Report/Update.pm:24
+#: perllib/FixMyStreet/App/Controller/Rss.pm:37
+msgid "Unknown problem ID"
+msgstr "Ukjent problem-Id"
-#: web-admin/index.cgi:158
-#, perl-format
-msgid "%d council contacts &ndash; %d confirmed, %d unconfirmed"
-msgstr "%d administrasjonskontakter &ndash; %d bekreftet, %d ubekreftet"
+#: templates/web/default/admin/timeline.html:35
+msgid "Update %s created for problem %d; by %s"
+msgstr "Oppdatering %s opprettet for problem %d, av %s"
-#: web-admin/index.cgi:163
-msgid "Graph of problem creation by status over time"
-msgstr "Graf over problemoppretting fordelt på status over tid"
+#: templates/web/default/contact/index.html:21
+#, fuzzy
+msgid "Update below added anonymously at %s"
+msgstr "Rapportert anonymt %s"
-#: web-admin/index.cgi:166
-msgid "Problem breakdown by state"
-msgstr "Tilstandsfordeling for problemer"
+#: templates/web/default/contact/index.html:23
+#, fuzzy
+msgid "Update below added by %s at %s"
+msgstr "Tilstandsfordeling for oppdateringer"
-#: web-admin/index.cgi:171
+#: templates/web/default/admin/index.html:29
msgid "Update breakdown by state"
msgstr "Tilstandsfordeling for oppdateringer"
-#: web-admin/index.cgi:187
-msgid "Diligency prize league table"
-msgstr "Hvem har endret kontaktlista"
-
-#: web-admin/index.cgi:191
-#, perl-format
-msgid "%d edits by %s"
-msgstr "%d redigeringer av %s"
+#: db/alert_types.pl:7
+msgid "Update by {{name}}"
+msgstr "Oppdatert av {{name}}"
-#: web-admin/index.cgi:194
-msgid "No edits have yet been made."
-msgstr "Ingenting er redigert."
+#: templates/web/default/admin/council_contacts.html:46
+msgid "Update statuses"
+msgstr "Oppdater tilstanden"
-#: web-admin/index.cgi:198
-msgid "Councils"
-msgstr "Administrasjoner"
+#: templates/web/default/report/display.html:103
+msgid "Update:"
+msgstr "Oppdatering:"
-#: web-admin/index.cgi:212 web-admin/index.cgi:587
-msgid "None"
-msgstr "Ingen"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:572
+#: perllib/FixMyStreet/App/Controller/Admin.pm:735
+msgid "Updated!"
+msgstr "Oppdatert!"
-#: web-admin/index.cgi:224
-#, perl-format
-msgid "%d addresses"
-msgstr "%d adresser"
+#: templates/web/default/admin/list_updates.html:1
+#: templates/web/default/report/updates.html:4
+msgid "Updates"
+msgstr "Oppdateringer"
-#: web-admin/index.cgi:230
-msgid "No info at all"
-msgstr "Helt uten informasjon"
+#: db/alert_types.pl:5 db/alert_types.pl:6
+msgid "Updates on {{title}}"
+msgstr "Oppdateringer av {{title}}"
-#: web-admin/index.cgi:232
-msgid "Currently has 1+ deleted"
-msgstr "For tiden har 1+ slettet"
+#: templates/web/default/report/display.html:0
+#: templates/web/default/report/display.html:7
+msgid "Updates to this problem, FixMyStreet"
+msgstr "Oppdateringer til dette problemet, FiksGataMi"
-#: web-admin/index.cgi:234
-msgid "Some unconfirmeds"
-msgstr "Noen ubekreftede"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:286
+#: perllib/FixMyStreet/App/Controller/Admin.pm:316
+msgid "Values updated"
+msgstr "Verdier oppdatert"
-#: web-admin/index.cgi:236
-msgid "All confirmed"
-msgstr "Alle bekreftet"
+#: templates/web/default/admin/report_edit.html:12
+#: templates/web/default/admin/update_edit.html:12
+msgid "View report on site"
+msgstr "Se rapport på nettstedet"
-#: web-admin/index.cgi:266 web-admin/index.cgi:276 web-admin/index.cgi:293
-msgid "*unknown*"
-msgstr "*ukjent*"
+#: templates/web/emptyhomes/tokens/confirm_problem.html:39
+msgid "View your report"
+msgstr "Vis din rapport"
-#: web-admin/index.cgi:269 web-admin/index.cgi:297
-msgid "Values updated"
-msgstr "Verdier oppdatert"
+#: templates/web/default/around/display_location.html:0
+#: templates/web/default/around/display_location.html:34
+msgid "Viewing a location"
+msgstr "Ser på et sted"
-#: web-admin/index.cgi:279
-msgid "New category contact added"
-msgstr "Ny kategorikontakt lagt til"
+#: templates/web/default/report/display.html:0
+msgid "Viewing a problem"
+msgstr "Ser på en sak"
-#: web-admin/index.cgi:316 web-admin/index.cgi:399
-#, perl-format
-msgid "Council contacts for %s"
-msgstr "Administrasjonskontakter for %s"
+#: perllib/FixMyStreet/Geocode.pm:155 perllib/FixMyStreet/Geocode.pm:174
+msgid ""
+"We do not cover Northern Ireland, I'm afraid, as our licence doesn't include "
+"any maps for the region."
+msgstr ""
+"Vi dekker ikke nordlige Irland, er jeg redd, da vår lisens ikke inkluderer "
+"noen kart for den regionen."
-#: web-admin/index.cgi:329
-msgid " List all reported problems"
-msgstr " List alle rapporterte problemer"
+#: templates/web/default/alert/choose.html:6
+#: templates/web/default/around/around_index.html:33
+msgid ""
+"We found more than one match for that location. We show up to ten matches, "
+"please try a different search if yours is not here."
+msgstr ""
+"Vi fant mer en ett treff for den plassen. Vi viser opp til ti treff, så "
+"forsøk et annet søk hvis din plass ikke er her."
-#: web-admin/index.cgi:331
-msgid "Text only version"
-msgstr "Tekst-versjon"
+#: perllib/FixMyStreet/App/Controller/Around.pm:235
+msgid "We had a problem with the supplied co-ordinates - outside the UK?"
+msgstr ""
-#: web-admin/index.cgi:337 web-admin/index.cgi:478
-msgid "Category"
-msgstr "Kategori"
+#: templates/web/emptyhomes/tokens/confirm_problem.html:18
+#: templates/web/emptyhomes/tokens/confirm_problem.html:20
+#, fuzzy
+msgid ""
+"We may contact you periodically to ask if anything has changed with the "
+"property you reported."
+msgstr ""
+"Det kan hende vi periodisk tar kontakt med deg for å spørre om noe har "
+"endret seg med eiedommen du rapporterte."
-#: web-admin/index.cgi:337 web-admin/index.cgi:439 web-admin/index.cgi:478
-#: web-admin/index.cgi:657
-msgid "Email"
-msgstr "Epost"
+#: bin/send-reports:185
+msgid ""
+"We realise this problem might be the responsibility of %s; however, we don't "
+"currently have any contact details for them. If you know of an appropriate "
+"contact address, please do get in touch."
+msgstr ""
+"Vi innser at %s kan være ansvarlig for dette problemet, men vi mangler for "
+"tiden kontaktinformasjon for dem. Hvis du vet om en egnet kontaktadresse, ta "
+"kontakt med oss."
-#: web-admin/index.cgi:337 web-admin/index.cgi:369 web-admin/index.cgi:422
-#: web-admin/index.cgi:439 web-admin/index.cgi:738
-msgid "Confirmed"
-msgstr "Bekreftet"
+#: templates/web/default/index.html:62
+msgid "We send it to the council on your behalf"
+msgstr "Vi sender til administrasjon på dine vegne"
-#: web-admin/index.cgi:337 web-admin/index.cgi:371 web-admin/index.cgi:424
-#: web-admin/index.cgi:439
-msgid "Deleted"
-msgstr "Slettet"
+#: templates/web/default/report/new/notes.html:5
+msgid ""
+"We will only use your personal information in accordance with our <a href=\"/"
+"faq#privacy\">privacy policy.</a>"
+msgstr ""
+"Vi vil kun bruke personlig informasjon om deg i henhold til vår <a href=\"/"
+"faq#privacy\">personvernpolicy.</a>"
-#: web-admin/index.cgi:337
-msgid "Last editor"
-msgstr "Sist redigert av"
+#: templates/web/emptyhomes/contact/blurb.html:2
+#, fuzzy
+msgid ""
+"We&rsquo;d love to hear what you think about this website. Just fill in the "
+"form. Please don&rsquo;t contact us about individual empty homes; use the "
+"box accessed from <a href=\"/\">the front page</a>."
+msgstr ""
+"Vi vil gjerne høre hva du tenker om dette nettstedet. Det er bare å fylle "
+"inn skjemaet. Vær så snill å ikke kontakte oss om individuelle tomme hjem. "
+"For det bør du i stedet bruke boksen tilgjengelig fra <a href=\"/"
+"\">forsiden</a>."
-#: web-admin/index.cgi:337 web-admin/index.cgi:439
-msgid "Note"
-msgstr "Merk"
+#: templates/web/default/contact/blurb.html:8
+msgid ""
+"We'd love to hear what you think about this site. Just fill in the form, or "
+"send an email to <a href='mailto:%s'>%s</a>:"
+msgstr ""
+"Vi ønsker å få din tilbakemelding om hva du mener om denne tjenesten. Bare "
+"fyll ut skjemaet, eller send en epost <a href='mailto:%s'>%s</a>:"
-#: web-admin/index.cgi:337 web-admin/index.cgi:439
+#: templates/web/default/admin/council_contacts.html:25
+#: templates/web/default/admin/council_edit.html:41
msgid "When edited"
msgstr "NÃ¥r redigert"
-#: web-admin/index.cgi:337
-msgid "Confirm"
-msgstr "Bekreftet"
-
-#: web-admin/index.cgi:354
-msgid "Update statuses"
-msgstr "Oppdater tilstanden"
+#: templates/web/default/admin/search_reports.html:22
+msgid "When sent"
+msgstr "NÃ¥r sendt"
-#: web-admin/index.cgi:359
-msgid "Add new category"
-msgstr "Legg til ny kategori"
+#: perllib/FixMyStreet/App/Controller/Report/New.pm:464
+msgid "Whole block of empty flats"
+msgstr "Hel blokk med tomme leiligheter"
-#: web-admin/index.cgi:362 web-admin/index.cgi:416
-msgid "Category: "
-msgstr "Kategori: "
+#: templates/web/default/questionnaire/index.html:104
+msgid ""
+"Would you like to receive another questionnaire in 4 weeks, reminding you to "
+"check the status?"
+msgstr ""
+"Kunne du tenke deg å motta en ny forespørsel om 4 uker, som minner deg om å "
+"sjekke status?"
-#: web-admin/index.cgi:365 web-admin/index.cgi:419
-msgid "Email: "
-msgstr "Epost: "
+#: templates/web/default/report/new/notes.html:8
+msgid ""
+"Writing your message entirely in block capitals makes it hard to read, as "
+"does a lack of punctuation."
+msgstr ""
+"Når du skriver meldingen din med kun store bokstaver blir den vanskelig å "
+"lese. Det samme gjelder manglende tegnsetting."
-#: web-admin/index.cgi:374 web-admin/index.cgi:427
-msgid "Note: "
-msgstr "Merk: "
+#: templates/web/default/admin/council_contacts.html:32
+#: templates/web/default/admin/council_contacts.html:33
+#: templates/web/default/admin/council_edit.html:5
+#: templates/web/default/admin/list_updates.html:28
+#: templates/web/default/admin/report_edit.html:18
+#: templates/web/default/admin/report_edit.html:37
+#: templates/web/default/admin/search_reports.html:43
+#: templates/web/default/admin/update_edit.html:15
+#: templates/web/default/questionnaire/creator_fixed.html:14
+#: templates/web/default/questionnaire/index.html:107
+#: templates/web/default/questionnaire/index.html:68
+msgid "Yes"
+msgstr "Ja"
-#: web-admin/index.cgi:381
-msgid "Create category"
-msgstr "Lag kategori"
+#: templates/web/default/contact/index.html:37
+msgid ""
+"You are reporting the following problem report for being abusive, containing "
+"personal information, or similar:"
+msgstr ""
+"Du rapporterer at følgende problem er støtende, inneholder personlig "
+"informasjon eller lignende:"
-#: web-admin/index.cgi:433
-msgid "Save changes"
-msgstr "Lagre endringer"
+#: templates/web/default/contact/index.html:15
+msgid ""
+"You are reporting the following update for being abusive, containing "
+"personal information, or similar:"
+msgstr ""
+"Du rapporterer at følgende oppdatering er støtende, inneholder personlig "
+"informasjon, eller lignende:"
-#: web-admin/index.cgi:437
-msgid "History"
-msgstr "Historie"
+#: templates/web/default/reports/council.html:35
+msgid ""
+"You can <a href=\"%s\">see less detail</a> or go back and <a href=\"/reports"
+"\">show all councils</a>."
+msgstr ""
+"Du kan <a href=\"%s\">se mindre detaljer</a> eller gå tilbake og <a href=\"/"
+"reports\">se alle administrasjonene</a>."
-#: web-admin/index.cgi:439
-msgid "Editor"
-msgstr "Oppdatert av"
+#: templates/web/default/reports/council.html:31
+msgid "You can <a href=\"%s\">see less detail</a>."
+msgstr "Du kan <a href=\"%s\">se mindre detaljer</a>."
-#: web-admin/index.cgi:443 web-admin/index.cgi:444
-msgid "yes"
-msgstr "ja"
+#: templates/web/default/reports/council.html:37
+msgid ""
+"You can <a href=\"%s\">see more details</a> or go back and <a href=\"/reports"
+"\">show all councils</a>."
+msgstr ""
+"Du kan <a href=\"%s\">se flere detaljer</a> eller gå tilbake og <a href=\"/"
+"reports\">se alle administrasjonene</a>."
-#: web-admin/index.cgi:443 web-admin/index.cgi:444
-msgid "no"
-msgstr "nei"
+#: templates/web/default/reports/council.html:33
+msgid "You can <a href=\"%s\">see more details</a>."
+msgstr "Du kan <a href=\"%s\">se flere detaljer</a>."
-#: web-admin/index.cgi:471
-msgid "Search:"
-msgstr "Søk:"
+#: templates/web/default/report/new/no_councils_text.html:14
+#: templates/web/default/report/new/no_councils_text.html:3
+#: templates/web/default/report/new/some_councils_text.html:20
+#: templates/web/default/report/new/some_councils_text.html:22
+msgid ""
+"You can help us by finding a contact email address for local problems for %s "
+"and emailing it to us at <a href='mailto:%s'>%s</a>."
+msgstr ""
+"Du kan hjelpe oss ved å finne en kontakt-epostadresse for lokale problemer i "
+"%s, og sende den via epost til oss på <a href='mailto:%s'>%s</a>."
-#: web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "ID"
-msgstr "ID"
+#: perllib/FixMyStreet/App/Controller/Questionnaire.pm:42
+msgid ""
+"You have already answered this questionnaire. If you have a question, please "
+"<a href='%s'>get in touch</a>, or <a href='%s'>view your problem</a>.\n"
+msgstr ""
+"Du har allerede besvart dette spørreskjemaet. Hvis du har spørsmål, "
+"vennligst <a href='%s'>ta kontakt</a>, eller <a href='%s'>se på ditt "
+"problem</a>.\n"
-#: web-admin/index.cgi:478
-msgid "Title"
-msgstr "Tittel"
+#: templates/web/default/questionnaire/index.html:94
+#: templates/web/default/report/display.html:122
+#: templates/web/default/report/new/fill_in_details.html:111
+msgid ""
+"You have already attached a photo to this report, attaching another one will "
+"replace it."
+msgstr ""
-#: web-admin/index.cgi:478
-msgid "Council"
-msgstr "Administrasjon"
+#: templates/web/default/auth/logout.html:3
+msgid "You have been logged out"
+msgstr ""
-#: web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "Cobrand"
+#: templates/web/default/report/new/fill_in_details.html:36
+#, fuzzy
+msgid ""
+"You have located the problem at the point marked with a purple pin on the "
+"map. If this is not the correct location, simply click on the map again. "
msgstr ""
+"Du har plassert problemet ved punktet i kartet som er markert med en lilla "
+"nål. Hvis dette ikke er korrekt plassering kan du ganske enkelt klikke på "
+"kartet på nytt."
-#: web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "Created"
-msgstr "Opprettet"
+#: templates/web/default/tokens/confirm_alert.html:7
+msgid "You have successfully confirmed your alert."
+msgstr "Du har nå lykkes med å bekrefte ditt varsel."
-#: web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "State"
-msgstr "Tilstand"
+#: templates/web/default/tokens/confirm_problem.html:6
+#: templates/web/default/tokens/confirm_problem.html:7
+msgid "You have successfully confirmed your problem"
+msgstr "Du har nå oppdatert din sak"
-#: web-admin/index.cgi:478
-msgid "When sent"
-msgstr "NÃ¥r sendt"
+#: templates/web/default/tokens/confirm_update.html:11
+#: templates/web/default/tokens/confirm_update.html:12
+msgid ""
+"You have successfully confirmed your update and you can now <a href=\"%s"
+"\">view it on the site</a>."
+msgstr ""
+"Du har nå bekreftet din oppdatering <a href=\"%s\">les om saken på portalen</"
+"a>."
-#: web-admin/index.cgi:478 web-admin/index.cgi:657
-msgid "*"
-msgstr "*"
+#: templates/web/default/tokens/confirm_alert.html:11
+#, fuzzy
+msgid "You have successfully created your alert."
+msgstr "Sletting av ditt varsel var vellykket."
-#: web-admin/index.cgi:500 web-admin/index.cgi:634
-msgid "Confirmed:"
-msgstr "Bekreftet:"
+#: templates/web/default/tokens/confirm_alert.html:9
+msgid "You have successfully deleted your alert."
+msgstr "Sletting av ditt varsel var vellykket."
-#: web-admin/index.cgi:501
-msgid "Fixed:"
-msgstr "Løst:"
+#: templates/web/default/email_sent.html:30
+msgid ""
+"You must now click the link in the email we've just sent you &mdash; if you "
+"do not, %s."
+msgstr ""
-#: web-admin/index.cgi:502
-msgid "Last&nbsp;update:"
-msgstr "Last&nbsp;oppdatering:"
+#: templates/web/default/admin/report_edit.html:32
+msgid "You really want to resend?"
+msgstr "Ønsker du virkelig å sende på nytt?"
-#: web-admin/index.cgi:518 web-admin/index.cgi:676
-msgid "Edit"
-msgstr "Rediger"
+#: templates/web/default/my/my.html:3
+#, fuzzy
+msgid "Your Reports"
+msgstr "Søk i rapporter"
-#: web-admin/index.cgi:543
-msgid "That problem will now be resent."
-msgstr "Det problemet vil nå bli sendt på nytt."
+#: templates/web/default/alert/list.html:123
+msgid "Your email:"
+msgstr "Din epost"
-#: web-admin/index.cgi:549
-msgid "I am afraid you cannot confirm unconfirmed reports."
-msgstr "Jeg er redd du ikke kan bekrefte ubekreftede rapporter."
+#: templates/web/default/contact/index.html:68
+msgid "Your name:"
+msgstr "Ditt navn:"
-#: web-admin/index.cgi:578 web-admin/index.cgi:698
-msgid "Updated!"
-msgstr "Oppdatert!"
+#: templates/web/default/contact/index.html:76
+msgid "Your&nbsp;email:"
+msgstr "Din&nbsp;epost:"
-#: web-admin/index.cgi:582
-#, perl-format
-msgid "Editing problem %d"
-msgstr "Rediger problem %d"
+#: templates/web/default/admin/timeline.html:6
+msgid "by %s"
+msgstr "av %s"
-#: web-admin/index.cgi:592
-msgid "used map"
-msgstr "brukte kart"
+#: templates/web/default/reports/council.html:6
+#: templates/web/default/reports/council.html:7
+msgid "council"
+msgstr "administrasjon"
-#: web-admin/index.cgi:592
+#: templates/web/default/admin/report_edit.html:15
msgid "didn't use map"
msgstr "brukte ikke kart"
-#: web-admin/index.cgi:604 web-admin/index.cgi:734
-msgid "Remove photo (can't be undone!)"
-msgstr "Fjern bilde (kan ikke gjøres om!)"
-
-#: web-admin/index.cgi:609
-msgid "Anonymous:"
-msgstr "Anonym:"
-
-#: web-admin/index.cgi:610 web-admin/index.cgi:738
-msgid "State:"
-msgstr "Tilstand:"
-
-#: web-admin/index.cgi:610
-msgid "Open"
-msgstr "Ã…pen"
-
-#: web-admin/index.cgi:610
-msgid "Fixed"
-msgstr "Løst"
-
-#: web-admin/index.cgi:610 web-admin/index.cgi:738
-msgid "Hidden"
-msgstr "Gjemt"
-
-#: web-admin/index.cgi:610 web-admin/index.cgi:738
-msgid "Unconfirmed"
-msgstr "Ubekreftet"
-
-#: web-admin/index.cgi:610
-msgid "Partial"
-msgstr "Delvis"
-
-#: web-admin/index.cgi:613
-msgid "You really want to resend?"
-msgstr "Ønsker du virkelig å sende på nytt?"
+#: perllib/Utils.pm:241
+msgid "less than a minute"
+msgstr "mindre enn et minutt"
-#: web-admin/index.cgi:613
-msgid "Resend report"
-msgstr "Send rapport på nytt"
+#: templates/web/default/report/updates.html:13
+msgid "marked as fixed"
+msgstr "markert som fikset"
-#: web-admin/index.cgi:622
-msgid "View report on site"
-msgstr "Se rapport på nettstedet"
+#: perllib/FixMyStreet/App/Controller/Admin.pm:107
+#: templates/web/default/admin/questionnaire.html:15
+#: templates/web/default/admin/questionnaire.html:16
+msgid "n/a"
+msgstr ""
-#: web-admin/index.cgi:625
-msgid "Co-ordinates:"
-msgstr "Koordinater:"
+#: templates/web/default/alert/list.html:119
+msgid "or"
+msgstr "eller"
-#: web-admin/index.cgi:625
+#: templates/web/default/admin/report_edit.html:15
msgid "originally entered"
msgstr "søkte etter"
-#: web-admin/index.cgi:626
-msgid "For council(s):"
-msgstr "For administrasjon(er):"
-
-#: web-admin/index.cgi:626
+#: templates/web/default/admin/report_edit.html:16
msgid "other areas:"
msgstr "andre områder:"
-#: web-admin/index.cgi:633 web-admin/index.cgi:754
-msgid "Created:"
-msgstr "Opprettet:"
+#: templates/web/default/report/updates.html:14
+msgid "reopened"
+msgstr "åpnet på nytt"
-#: web-admin/index.cgi:635
-msgid "Sent:"
-msgstr "Sendt:"
+#: perllib/FixMyStreet/DB/Result/Problem.pm:361
+msgid "the map was not used so pin location may be inaccurate"
+msgstr "kartet ble ikke brukt, så nåleposisjon kan være unøyaktig"
-#: web-admin/index.cgi:636
-msgid "Last update:"
-msgstr "Siste oppdatering:"
+#: bin/send-reports:166
+msgid "this type of local problem"
+msgstr "denne type lokalt problem"
-#: web-admin/index.cgi:637
-msgid "Service:"
-msgstr "Tjeneste:"
+#: perllib/Utils.pm:217
+msgid "today"
+msgstr "idag"
-#: web-admin/index.cgi:638 web-admin/index.cgi:752
-msgid "Cobrand:"
-msgstr ""
+#: templates/web/default/admin/report_edit.html:15
+msgid "used map"
+msgstr "brukte kart"
-#: web-admin/index.cgi:639 web-admin/index.cgi:753
-msgid "Cobrand data:"
+#: templates/web/default/reports/council.html:0
+#: templates/web/default/reports/council.html:3
+msgid "ward"
msgstr ""
-#: web-admin/index.cgi:640
-msgid "Going to send questionnaire?"
-msgstr "Skal det sendes spørreskjema?"
-
-#: web-admin/index.cgi:644 web-admin/index.cgi:758
-msgid "Submit changes"
-msgstr "Send inn endringer"
-
-#: web-admin/index.cgi:657
-msgid "Text"
-msgstr "Tekst"
-
-#: web-admin/index.cgi:703
-msgid "Problem marked as open."
-msgstr "Problem markert som åpent."
-
-#: web-admin/index.cgi:719
-#, perl-format
-msgid "Editing update %d"
-msgstr "Redigerer oppdatering %d"
-
-#: web-admin/index.cgi:747
-msgid "View update on site"
-msgstr "Vis oppdateringer på nettstedet"
+#: templates/web/default/email_sent.html:15
+#: templates/web/default/email_sent.html:3
+msgid "we'll hang on to your alert while you're checking your email."
+msgstr "vi holder på ditt varsel mens du sjekker din epost."
-#: web-admin/index.cgi:748
-msgid "Text:"
-msgstr "Tekst:"
+#: templates/web/default/email_sent.html:3
+#: templates/web/default/email_sent.html:7
+msgid "we'll hang on to your problem report while you're checking your email."
+msgstr "vi holder på rapporten din mens du sjekker epost."
-#: web-admin/index.cgi:750
-msgid "(blank to go anonymous)"
-msgstr "(blank for å være anonym)"
+#: templates/web/default/email_sent.html:11
+#: templates/web/default/email_sent.html:3
+msgid "we'll hang on to your update while you're checking your email."
+msgstr "vi holder på din oppdatering mens du sjekker din epost."
-#: web-admin/index.cgi:832 web-admin/index.cgi:835
-#, perl-format
-msgid "by %s"
-msgstr "av %s"
+#: templates/web/default/email_sent.html:14
+#: templates/web/default/email_sent.html:3
+msgid "your alert will not be activated"
+msgstr "vil ditt varsel ikke bli aktivisert"
-#: web-admin/index.cgi:833
-#, perl-format
-msgid "Problem %d created"
-msgstr "Problem %d opprettet"
+#: templates/web/default/email_sent.html:3
+#: templates/web/default/email_sent.html:6
+msgid "your problem will not be posted"
+msgstr "vil ditt problem ikke bli publisert"
-#: web-admin/index.cgi:838
-#, perl-format
-msgid "Problem %s confirmed"
-msgstr "Problem %s bekreftet"
+#: templates/web/default/email_sent.html:10
+#: templates/web/default/email_sent.html:3
+msgid "your update will not be posted"
+msgstr "din oppdatering vil ikke bli publisert"
-#: web-admin/index.cgi:842
+#: templates/web/default/front_stats.html:19
#, perl-format
-msgid "Problem %s sent to council %s"
-msgstr "Problem %s sendt til administrasjon %s"
+msgid "<big>%s</big> report recently"
+msgid_plural "<big>%s</big> reports recently"
+msgstr[0] "<big>%s</big> rapportert<br>nylig"
+msgstr[1] "<big>%s</big> rapportert<br>nylig"
-#: web-admin/index.cgi:844
+#: templates/web/default/report/new/no_councils_text.html:5
#, perl-format
-msgid "Questionnaire %d sent for problem %d"
-msgstr "Spørreskjema %d sendt for problem %d"
+msgid "We do not yet have details for the council that covers this location."
+msgid_plural ""
+"We do not yet have details for the councils that cover this location."
+msgstr[0] ""
+"Vi har ennå ikke detaljer for administrasjonen som dekker dette stedet."
+msgstr[1] ""
+"Vi har ennå ikke detaljer for administrasjonene som dekker dette stedet."
-#: web-admin/index.cgi:846
+#: templates/web/default/front_stats.html:14
#, perl-format
-msgid "Questionnaire %d answered for problem %d, %s to %s"
-msgstr "Spørreskjema %d fylt inn for problem %d, %s til %s"
+msgid "<big>%s</big> report in past week"
+msgid_plural "<big>%s</big> reports in past week"
+msgstr[0] "<big>%s</big> rapport siste uke"
+msgstr[1] "<big>%s</big> rapporter siste uke"
-#: web-admin/index.cgi:851
+#: templates/web/default/front_stats.html:25
#, perl-format
-msgid "Update %s created for problem %d; by %s"
-msgstr "Oppdatering %s opprettet for problem %d, av %s"
+msgid "<big>%s</big> fixed in past month"
+msgid_plural "<big>%s</big> fixed in past month"
+msgstr[0] "<big>%s</big> fikset siste måned"
+msgstr[1] "<big>%s</big> fikset siste måned"
-#: web-admin/index.cgi:855
+#: templates/web/default/report/new/some_councils_text.html:14
#, perl-format
-msgid "Alert %d created for %s, type %s, parameters %s / %s"
-msgstr "Varsel %d opprettet for %s, type %s, parameter %s / %s"
+msgid ""
+"We do <strong>not</strong> yet have details for the other council that "
+"covers this location."
+msgid_plural ""
+"We do <strong>not</strong> yet have details for the other councils that "
+"cover this location."
+msgstr[0] ""
+"Vi har ennå <strong>ikke</strong> detaljene for den andre administrasjonen "
+"som dekker dette stedet."
+msgstr[1] ""
+"Vi har ennå <strong>ikke</strong> detaljene for de andre administrasjonene "
+"som dekker dette stedet."
-#: web-admin/index.cgi:858
+#: templates/web/default/front_stats.html:31
#, perl-format
-msgid "Alert %d disabled (created %s)"
-msgstr "Varsel %d koblet ut (opprettet %s)"
+msgid "<big>%s</big> update on reports"
+msgid_plural "<big>%s</big> updates on reports"
+msgstr[0] "<big>%s</big> rapport-<br>oppdatering"
+msgstr[1] "<big>%s</big> rapport-<br>oppdateringer"
-#: web-admin/index.cgi:890
-#, fuzzy
-msgid "Not reported before"
-msgstr "Rapportert tidligere"
+#~ msgid "Sorry! Something's gone wrong."
+#~ msgstr "Beklager! Noe har gått galt."
+
+#~ msgid "The text of the error was:"
+#~ msgstr "Teksten i feilmeldingen var:"
+
+#~ msgid "FixMyStreet"
+#~ msgstr "FiksGataMi"
+
+#~ msgid ""
+#~ "<h1>Nearly Done! Now check your email...</h1>\n"
+#~ "<p>The confirmation email <strong>may</strong> take a few minutes to "
+#~ "arrive &mdash; <em>please</em> be patient.</p>\n"
+#~ "<p>If you use web-based email or have 'junk mail' filters, you may wish "
+#~ "to check your bulk/spam mail folders: sometimes, our messages are marked "
+#~ "that way.</p>\n"
+#~ "<p>You must now click the link in the email we've just sent you &mdash;\n"
+#~ "if you do not, %s.</p>\n"
+#~ "<p>(Don't worry &mdash; %s)</p>\n"
+#~ msgstr ""
+#~ "<h1>Nesten ferdig! Nå må du sjekke eposten din...</h1>\n"
+#~ "<p>Bekreftelseseposten <strong>kan</strong> bruke noen minutter på å nå "
+#~ "frem &mdash; så vær tålmodig.</p>\n"
+#~ "<p>Hvis du bruker webbasert epost eller har søppelepost-filter, så bør du "
+#~ "kanskje a sjekke din bulk/spam-mappe. Noen ganger blir epost fra oss "
+#~ "markert som søppelpost.</p>\n"
+#~ "<p>Du må klikke på lenken i eposten vi nettopp har sendt deg &mdash;\n"
+#~ "hvis du ikke gjør det, %s.</p>\n"
+#~ "<p>(Ingen grunn til bekymring &mdash; %s)</p>\n"
+
+#~ msgid "More than one match"
+#~ msgstr "Mer enn ett treff"
+
+#~ msgid ""
+#~ "Thank you for trying to confirm your alert. We seem to have an error "
+#~ "ourselves\n"
+#~ "though, so <a href=\"%s\">please let us know what went on</a> and we'll "
+#~ "look into it.\n"
+#~ msgstr ""
+#~ "Takk for at du forsøker å bekrefte ditt varsel. Vi ser ut til a ha en "
+#~ "feil hos oss,\n"
+#~ "så <a href=\"%s\">vær så snill å fortell oss hva som skjedde</a> så skal "
+#~ "vi se på saken.\n"
+
+#~ msgid "Local RSS feeds and email alerts for &lsquo;%s&rsquo;"
+#~ msgstr "Lokal RSS-strøm og epostvarsel for &lsquo;%s&rsquo;"
+
+#~ msgid "Receive alerts on new local problems"
+#~ msgstr "Motta varsel om nye lokale problemer"
+
+#~ msgid "You have successfully subscribed to that alert."
+#~ msgstr "Du abonnerer nå på denne saken"
+
+#~ msgid "We could not validate that alert."
+#~ msgstr "Vi kunne ikke validere det varselet."
+
+#~ msgid "Please give your name"
+#~ msgstr "Skriv inn ditt navn"
+
+#~ msgid "Please give a subject"
+#~ msgstr "Skriv inn emne"
+
+#~ msgid "Weird and Wonderful reports"
+#~ msgstr "Merkelige saker"
+
+#~ msgid "That location is not part of that council"
+#~ msgstr "Det stedet er ikke en del av den administrasjonen"
+
+#~ msgid ""
+#~ "You have successfully confirmed your report and you can now <a href=\"%s"
+#~ "\">view it on the site</a>."
+#~ msgstr ""
+#~ "Du har nå lagt til en sak og kan <a href=\"%s\">se saken på nettsiden</a>."
+
+#~ msgid ""
+#~ "All the information you provide here will be sent to <strong>%s</"
+#~ "strong>.\n"
+#~ "The subject and details of the problem will be public, plus your\n"
+#~ "name if you give us permission."
+#~ msgstr ""
+#~ "All informasjonen du gir oss her vil bli sendt til <strong>%s</strong>.\n"
+#~ "Tittelen og detaljene for problemet vil bli offentlig, pluss ditt navn\n"
+#~ "hvis du gir oss tillatelse."
+
+#~ msgid "Receive email when updates are left on this problem"
+#~ msgstr "Motta epost når oppdateringer legges inn om dette problemet"
+
+#~ msgid "New problems within {{NAME}}"
+#~ msgstr "Nye problemer innenfor {{NAME}}"
+
+#~ msgid "The latest problems within {{NAME}}"
+#~ msgstr "De siste problemer innenfor {{NAME}}"
+
+#~ msgid "View update on site"
+#~ msgstr "Vis oppdateringer på nettstedet"
+
+#~ msgid "(blank to go anonymous)"
+#~ msgstr "(blank for å være anonym)"
-#. Please leave the first word "Subject:" untranslated
-#: templates/emails/alert-confirm
-msgid ""
-"Subject: Confirm your alert on FixMyStreet\n"
-"\n"
-"Hi,\n"
-"\n"
-"Please click on the link below to confirm the alert you just\n"
-"asked to subscribe to on FixMyStreet:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"If you can't click the link, please copy and paste it to the\n"
-"address bar of your web browser.\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-msgstr ""
-"Subject: Bekreft din sak på FiksGataMI.no\n"
-"\n"
-"Hei,\n"
-"\n"
-"Vennligst klikk på lenken under for å bekrefte varslet du\n"
-"har bedt om å abonnere på FiksGataMi:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"Hvis du ikke kan klikke på lenken, kan du kopiere den i\n"
-"addressefeltet på dine nettleser.\n"
-"\n"
-"Vennlig Hilsen, \n"
-"FiksGataMi-gruppen\n"
-
-#: templates/emails/alert-problem
-msgid ""
-"Subject: New problems on FixMyStreet\n"
-"\n"
-"The following new problems have been added:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"To stop receiving emails when there are new problems,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-"Subject: Ny saker på FiksGataMi.no\n"
-"\n"
-"Følgende ny saker er lagt til:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Vennlig hilsen, \n"
-"FiksGataMi-gruppen\n"
-"\n"
-"Hvis du ikke ønsker å stå på denne epostlisten,\n"
-"klikk på linken: <?=$values['unsubscribe_url']?>\n"
-
-#: templates/emails/alert-problem-area
-msgid ""
-"Subject: New problems in <?=$values['area_name']?> on FixMyStreet\n"
-"\n"
-"The following new problems have been added within\n"
-"<?=$values['area_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"To stop receiving emails when there are new problems in\n"
-"<?=$values['area_name']?>, please follow this link: \n"
-"<?=$values['unsubscribe_url']?>\n"
-msgstr ""
-"Subject: Nye saker i <?=$values['area_name']?> sendt via FiksGataMi.no\n"
-"\n"
-"Følgende saker er sendt til\n"
-"<?=$values['area_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Vennlig hilsen, \n"
-"FiksGataMi-gruppen\n"
-"\n"
-"Hvis du ikke ønsker å motta epost om saker som er sendt til\n"
-"<?=$values['area_name']?> i fremtiden, klikk på følgende link: \n"
-"<?=$values['unsubscribe_url']?>\n"
-
-#: templates/emails/alert-problem-council
-msgid ""
-"Subject: New problems reported to <?=$values['area_name']?> on FixMyStreet\n"
-"\n"
-"The following new problems have been reported to <?=$values['area_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"To stop receiving emails when there are new problems reported to\n"
-"<?=$values['area_name']?>, please follow this link: \n"
-"<?=$values['unsubscribe_url']?>\n"
-msgstr ""
-"Subject: Nye saker sendt til <?=$values['area_name']?> via FiksGataMi.no\n"
-"\n"
-"Følgende saker er sendt til <?=$values['area_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Vennlig hilsen, \n"
-"FiksGataMi-gruppen\n"
-"\n"
-"Hvis du ikke ønsker å motta epost om saker som er sendt til\n"
-"<?=$values['area_name']?> i fremtiden, klikk på følgende link: \n"
-"<?=$values['unsubscribe_url']?>\n"
-
-#: templates/emails/alert-problem-nearby
-msgid ""
-"Subject: New nearby problems on FixMyStreet\n"
-"\n"
-"The following nearby problems have been added:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"To stop receiving emails when there are nearby problems,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-"Subject: Nye saker i nærheten på FiksGataMi.no\n"
-"\n"
-"Følgende saker i nærheten er sendt via FiksGataMi.no:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Vennlig hilsen, \n"
-"FiksGataMi-gruppen\n"
-"\n"
-"Hvis du ikke ønsker å abonnere på epost om problemer i nærheten,\n"
-"klikk på følgende link: <?=$values['unsubscribe_url']?>\n"
-
-#: templates/emails/alert-problem-ward
-msgid ""
-"Subject: New problems reported to <?=$values['area_name']?> within <?=$values"
-"['ward_name']?> on FixMyStreet\n"
-"\n"
-"The following new problems have been reported to <?=$values['area_name']?>\n"
-"within <?=$values['ward_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"To stop receiving emails when there are new problems reported to\n"
-"<?=$values['area_name']?> within <?=$values['ward_name']?>,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-"Subject: Nye saker sendt til <?=$values['area_name']?> innenfor <?=$values "
-"['ward_name']?> via FiksGataMi.no\n"
-"\n"
-"Følgende saker er sendt til <?=$values['area_name']?>\n"
-"innenfor <?=$values['ward_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Vennlig hilsen, \n"
-"FiksGataMi-gruppen\n"
-"\n"
-"Hvis du ikke ønsker å abonnere via epost på saker som er sendt til\n"
-"<?=$values['area_name']?> innenfor <?=$values['ward_name']?>,\n"
-"klikk på denne linken: <?=$values['unsubscribe_url']?>\n"
-
-#: templates/emails/alert-update
-msgid ""
-"Subject: New updates on problem - '<?=$values['title']?>'\n"
-"\n"
-"The following updates have been left on this problem:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"<?=$values['state_message']?>\n"
-"\n"
-"To view or reply to these updates, please visit the following URL:\n"
-" <?=$values['problem_url']?>\n"
-"\n"
-"You cannot contact anyone by replying to this email.\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"To stop receiving emails when there are new updates on this problem,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-"Subject: Nye oppdateringer for problem - '<?=$values['title']?>'\n"
-"\n"
-"Følgende oppdateringer har blitt lagt inn for dette problemet:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"<?=$values['state_message']?>\n"
-"\n"
-"For å se på eller svare på disse oppdateringene, besøk denne URLen:\n"
-" <?=$values['problem_url']?>\n"
-"\n"
-"Du kan ikke kontakte noen ved å svare på denne eposten.\n"
-"\n"
-"Vennlig hilsen, \n"
-"Fiksgata-laget\n"
-"\n"
-"For å slutte å motta epost når det er nye oppdateringer om dettte "
-"problemet,\n"
-"følg denne lenken: <?=$values['unsubscribe_url']?>\n"
-
-#: templates/emails/empty property-confirm
-msgid ""
-"Subject: Confirm your empty property report\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"Please click on the link below to confirm the empty property\n"
-"report you just added to the site:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"If your email program does not let you click on this link,\n"
-"copy and paste it into your web browser and press return.\n"
-"\n"
-"Your report had the subject:\n"
-"<?=$values['title']?>\n"
-"\n"
-"And details:\n"
-"<?=$values['detail']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-msgstr ""
-
-#: templates/emails/flickr-confirm
-msgid ""
-"Subject: Confirm your email address on FixMyStreet\n"
-"\n"
-"Hi,\n"
-"\n"
-"Please click on the link below to confirm the email address\n"
-"you just gave to FixMyStreet:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"This is so we can look up the photos you tag with FixMyStreet,\n"
-"and send you an email letting you know about your new problems.\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-msgstr ""
-"Subject: Bekreft din epostadresse på FiksGataMi.no\n"
-"\n"
-"Hei,\n"
-"\n"
-"Klikk på linken under for å bekrefte epostadressen\n"
-"du akkurat la inn på FiksGataMI.no:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"\n"
-"Vennlig hilsen, \n"
-"FiksGataMi-gruppen\n"
-
-#: templates/emails/flickr-submit
-msgid ""
-"Subject: New photo pulled from Flickr to FixMyStreet\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"We've fetched a photo you uploaded to Flickr and tagged with\n"
-"FixMyStreet. To check the details we have, and to add any more,\n"
-"please visit the following URL:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"Then we can send your photo to the council. Thanks!\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-msgstr ""
-"Subject: Nye bilder hentet fra Flickr til FiksGataMi.no\n"
-"\n"
-"Hei <?=$values['name']?>,\n"
-"\n"
-"Vi har hentet et bilde som du lastet opp på Flickr og merket det med\n"
-"FiksGataMi. For å sjekke dataene vi har, og evt. legge til flere,\n"
-"klikk på følgende link:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"På denne måten kan vi send bildene til administrasjonen. Takk!\n"
-"\n"
-"Vennlig hilsen, \n"
-"FiksGataMi-gruppen\n"
-
-#: templates/emails/partial
-msgid ""
-"Subject: Confirm your report on FixMyStreet\n"
-"\n"
-"Hi<?=$values['name']?>,\n"
-"\n"
-"To confirm the report you have uploaded to FixMyStreet via\n"
-"<?=$values['service']?>, and to check or add any details,\n"
-"please visit the following URL:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"Thanks!\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-msgstr ""
-"Subject: Bekreft din rapport på FiksGataMi\n"
-"\n"
-"Hei<?=$values['name']?>,\n"
-"\n"
-"For å bekrefte rapporten du har lastet opp til FiksGatami via\n"
-"<?=$values['service']?>, og for å sjekke eller legge til litt detaljer,\n"
-"besøk følgende URL:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"Takk!\n"
-"\n"
-"Vennlig hilsen, \n"
-"FiksGataMi-gruppen\n"
-
-#: templates/emails/problem-confirm
-msgid ""
-"Subject: Confirm your problem on FixMyStreet\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"Please click on the link below to confirm the problem you just\n"
-"added to FixMyStreet:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"If your email program does not let you click on this link,\n"
-"copy and paste it into your web browser and press return.\n"
-"\n"
-"Your problem had the title:\n"
-"<?=$values['title']?>\n"
-"\n"
-"And details:\n"
-"<?=$values['detail']?>\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-msgstr ""
-"Subject: Bekreftelse på ny sak lagt til på FiksGataMi.no\n"
-"\n"
-"Hei <?=$values['name']?>,\n"
-"\n"
-"Vennligst klikk på linken under for å bekrefte saken du har lagt til\n"
-"på FiksGataMi:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"Hvis ditt epost program ikke gir deg mulighet til å klikke på linken,\n"
-"kopier linken inn i nettleseren.\n"
-"\n"
-"Din sak hadde følgende tittel:\n"
-"<?=$values['title']?>\n"
-"\n"
-"Med følgende verdier:\n"
-"<?=$values['detail']?>\n"
-"\n"
-"Vennlig hilsen, \n"
-"FiksGataMi-gruppen\n"
-
-#: templates/emails/questionnaire
-msgid ""
-"Subject: Questionnaire about your problem on FixMyStreet\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"<?=$values['created']?> ago, you left a problem on FixMyStreet\n"
-"with the details provided at the end of this email. To keep our\n"
-"site up to date and relevant, we'd appreciate it if you could\n"
-"follow the link below and fill in our short questionnaire\n"
-"updating the status of your problem:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"Please do not reply to this email; there is a public comment\n"
-"box on the questionnaire.\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"Your problem was as follows:\n"
-"\n"
-"<?=$values['title']?>\n"
-"\n"
-"<?=$values['detail']?>\n"
-"\n"
-msgstr ""
-"Subject: Skjema for din sak på FiksGataMi\n"
-"\n"
-"Hei <?=$values['name']?>,\n"
-"\n"
-"for <?=$values['created']?> siden, la du til en sak på FiksGataMi\n"
-"med detaljene som vist i denne eposten. For å holde vår nettside oppdatert\n"
-"og relevant, vil vi sette pris på om du kunne fylle ut følgende skjema for "
-"Ã¥\n"
-"oppdatere saken:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"Vennligst ikke svar på denne eposten; det er muligheter for å kommentere i "
-"skjema.\n"
-"\n"
-"Vennlig hilsen, \n"
-"FiksGataMi-gruppen\n"
-"\n"
-"Saken du la til var som følger:\n"
-"\n"
-"<?=$values['title']?>\n"
-"\n"
-"<?=$values['detail']?>\n"
-"\n"
-
-#: templates/emails/questionnaire-eha-26weeks
-msgid ""
-"Subject: Questionnaire about your empty property report\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"Six months ago, you reported an empty home on ReportEmptyHomes.com with the\n"
-"details provided at the end of this email. To keep our site up to date and\n"
-"relevant, I'd be grateful if you could fill in this short questionnaire to "
-"tell\n"
-"us what has happened: \n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"Please do not reply to this email; there is a public comment\n"
-"box on the questionnaire.\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"Your report was as follows:\n"
-"\n"
-"<?=$values['title']?>\n"
-"\n"
-"Property type: <?=$values['category']?>\n"
-"\n"
-"<?=$values['detail']?>\n"
-"\n"
-msgstr ""
-
-#: templates/emails/questionnaire-eha-4weeks
-msgid ""
-"Subject: Questionnaire about your empty property report\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"Four weeks ago, you reported an empty home on ReportEmptyHomes.com with the\n"
-"details provided at the end of this email. To keep our site up to date and\n"
-"relevant, I'd be grateful if you could fill in this short questionnaire to "
-"tell\n"
-"us what has happened:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"Please do not reply to this email; there is a public comment\n"
-"box on the questionnaire.\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"Your report was as follows:\n"
-"\n"
-"<?=$values['title']?>\n"
-"\n"
-"Property type: <?=$values['category']?>\n"
-"\n"
-"<?=$values['detail']?>\n"
-"\n"
-msgstr ""
-
-#: templates/emails/reply-autoresponse
-msgid ""
-"Subject: Automatic reply to your message to FixMyStreet\n"
-"\n"
-"Hi,\n"
-"\n"
-"This is an automatic response to your email; your email has not been "
-"delivered.\n"
-"\n"
-"If you're replying to an email about a report update, please visit\n"
-"the URL given in the email in order to leave a reply. You cannot\n"
-"reply to an update via email.\n"
-"\n"
-"If you are trying to confirm something, such as a report or an email\n"
-"alert, please click the link in the email that we sent you, or, if\n"
-"you cannot click the link, copy and paste it into the address bar of\n"
-"your web browser.\n"
-"\n"
-"If you're trying to unsubscribe from an email alert, there is an\n"
-"unsubscribe link at the bottom of the email.\n"
-"\n"
-"If you have a question or comment about the site, please send your\n"
-"email to team@fixmystreet.com\n"
-"\n"
-"Yours,\n"
-"The FixMyStreet team\n"
-msgstr ""
-"Subject: Automatisk svar på din melding til FiksGataMi\n"
-"\n"
-"Hei,\n"
-"\n"
-"Dette er et automatisk svar på din epost. Din epost er ikke levert.\n"
-"\n"
-"Hvis du svarer på en epost om en rapportoppdatering, vennligst\n"
-"besøk URLen oppgitt i eposten for å legge igjen et svar. Du kan\n"
-"ikke svare på en oppdatering via epost.\n"
-"\n"
-"Hvis du forsøker å bekrefte noe, som en rapport eller et epostvarsel,\n"
-"så må du klikke på lenken i eposten vi sendte deg, eller, hvis du ikke\n"
-"kan klikke på lenken, kopier og lim den inn i adressefeltet på din "
-"nettleser.\n"
-"\n"
-"Hvis du forsøker å melde deg av et epostvarsel, så er det en\n"
-"avmeldings-lenke i slutten av eposten.\n"
-"\n"
-"Hvis du har et spørsmål eller en kommentar om nettstedet, vennligst send din "
-"epost til fiksgatami@nuug.no\n"
-"\n"
-"Vennlig hilsen,\n"
-"FiksGataMi-gruppen\n"
-
-#: templates/emails/submit-brent
-msgid ""
-"Subject: FMS Problem Report: <?=$values['title']?>\n"
-"\n"
-"Dear <?=$values['councils_name']?>,\n"
-"\n"
-"<?=$values['missing']?><?=$values['multiple']?>A user of\n"
-"FixMyStreet has submitted the following report\n"
-"of a local problem that they believe might require your attention.\n"
-"\n"
-"<?=$values['fuzzy']?>, or to provide an update on the problem,\n"
-"please visit the following link:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"<?=$values['has_photo']?>----------\n"
-"\n"
-"Name: <?=$values['name']?>\n"
-"\n"
-"Email: <?=$values['email']?>\n"
-"\n"
-"<?=$values['phone_line']?><?=$values['category_line']?>Subject: <?=$values"
-"['title']?>\n"
-"\n"
-"Details: <?=$values['detail']?>\n"
-"\n"
-"<?=$values['easting_northing']?>Latitude: <?=$values['latitude']?>\n"
-"\n"
-"Longitude: <?=$values['longitude']?>\n"
-"\n"
-"<?=$values['closest_address_machine']?>----------\n"
-"\n"
-"Replies to this email will go to the user who submitted the problem.\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"[ This message was sent via FixMyStreet, a project of UKCOD, registered "
-"charity\n"
-"number 1076346. If there is a more appropriate email address for messages "
-"about\n"
-"<?=$values['category_footer']?>, please let us know by visiting <http://www."
-"fixmystreet.com/contact>.\n"
-"This will help improve the service for local people. We\n"
-"also welcome any other feedback you may have. ]\n"
-"\n"
-msgstr ""
-"Subject: FMS problemrapport: <?=$values['title']?>\n"
-"\n"
-"Til <?=$values['councils_name']?>,\n"
-"\n"
-"<?=$values['missing']?><?=$values['multiple']?>En bruker av\n"
-"FiksGataMi har sendt inn følgende rappport\n"
-"om et lokalt problem som vi tror trenger deres oppmerksomhet.\n"
-"\n"
-"<?=$values['fuzzy']?>, eller for å bidra med en oppdatering om problemet,\n"
-"vennligst besøk følgende lenke:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"<?=$values['has_photo']?>----------\n"
-"\n"
-"Navn: <?=$values['name']?>\n"
-"\n"
-"Epost: <?=$values['email']?>\n"
-"\n"
-"<?=$values['phone_line']?><?=$values['category_line']?>Tema: <?=$values"
-"['title']?>\n"
-"\n"
-"Detaljer: <?=$values['detail']?>\n"
-"\n"
-"<?=$values['easting_northing']?>Breddegrad: <?=$values['latitude']?>\n"
-"\n"
-"Lengdegrad: <?=$values['longitude']?>\n"
-"\n"
-"<?=$values['closest_address_machine']?>----------\n"
-"\n"
-"Svar på denne eposten vil gå til brukeren som sendte inn problemet.\n"
-"\n"
-"Vennlig hilsen, \n"
-"Fiksgatami-gruppen\n"
-"\n"
-"[ Denne meldingen ble sendt inn via FiksGataMi, et prosjekt hos foreningen "
-"NUUG.\n"
-"Hvis det er en mer passende epostadresse for meldinger om\n"
-"<?=$values['category_footer']?>, vær så snill å gi oss beskjed ved å besøke\n"
-"<http://www.fiksgatami.no/contact>.\n"
-"Dette vil bidra til å forbedre tjenesten for befolkningen. Vi\n"
-"tar også gjerne imot andre tilbakemeldinger som du har noen. ]\n"
-"\n"
-
-#: templates/emails/submit-council
-msgid ""
-"Subject: Problem Report: <?=$values['title']?>\n"
-"\n"
-"Dear <?=$values['councils_name']?>,\n"
-"\n"
-"<?=$values['missing']?><?=$values['multiple']?>A user of\n"
-"FixMyStreet has submitted the following report\n"
-"of a local problem that they believe might require your attention.\n"
-"\n"
-"<?=$values['fuzzy']?>, or to provide an update on the problem,\n"
-"please visit the following link:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"<?=$values['has_photo']?>----------\n"
-"\n"
-"Name: <?=$values['name']?>\n"
-"\n"
-"Email: <?=$values['email']?>\n"
-"\n"
-"<?=$values['phone_line']?><?=$values['category_line']?>Subject: <?=$values"
-"['title']?>\n"
-"\n"
-"Details: <?=$values['detail']?>\n"
-"\n"
-"<?=$values['easting_northing']?>Latitude: <?=$values['latitude']?>\n"
-"\n"
-"Longitude: <?=$values['longitude']?>\n"
-"\n"
-"<?=$values['closest_address']?>----------\n"
-"\n"
-"Replies to this email will go to the user who submitted the problem.\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-"\n"
-"[ This message was sent via FixMyStreet, a project of UKCOD, registered "
-"charity\n"
-"number 1076346. If there is a more appropriate email address for messages "
-"about\n"
-"<?=$values['category_footer']?>, please let us know by visiting <http://www."
-"fixmystreet.com/contact>.\n"
-"This will help improve the service for local people. We\n"
-"also welcome any other feedback you may have. ]\n"
-"\n"
-msgstr ""
-"Subject: Problemrapport: <?=$values['title']?>\n"
-"\n"
-"Til <?=$values['councils_name']?>,\n"
-"\n"
-"<?=$values['missing']?><?=$values['multiple']?>En bruker av\n"
-"FiksGataMi har sendt inn følgende rapport om et lokalt\n"
-"problem som vi tror trenger deres oppmerksomhet.\n"
-"\n"
-"<?=$values['fuzzy']?>, eller for å bidra med en oppdatering om problemet,\n"
-"vennligst besøk følgende lenke:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"<?=$values['has_photo']?>----------\n"
-"\n"
-"Navn: <?=$values['name']?>\n"
-"\n"
-"Epost: <?=$values['email']?>\n"
-"\n"
-"<?=$values['phone_line']?><?=$values['category_line']?>Tema: <?=$values"
-"['title']?>\n"
-"\n"
-"Detajer: <?=$values['detail']?>\n"
-"\n"
-"<?=$values['easting_northing']?>Breddegrad: <?=$values['latitude']?>\n"
-"\n"
-"Lengegrad: <?=$values['longitude']?>\n"
-"\n"
-"<?=$values['closest_address']?>----------\n"
-"\n"
-"Svar på denne eposten går til brukeren som sendte inn problemet.\n"
-"\n"
-"Vennlig hilsen, \n"
-"FiksGataMi-gruppen\n"
-"\n"
-"[ Denne meldingen ble sendt inn via FiksGataMi, et prosjekt hos foreningen "
-"NUUG.\n"
-"Hvis det er en mer passende epostadresse for meldinger om\n"
-"<?=$values['category_footer']?>, vær så snill å gi oss beskjed ved å besøke "
-"<http://www.fiksgatami.no/contact>.\n"
-"Dette vil bidra til å forbedre tjenesten for lokalbefolkningen. Vi\n"
-"tar også gjerne imot andre tilbakemeldinger om du har noen. ]\n"
-"\n"
-
-#: templates/emails/submit-eha
-msgid ""
-"Subject: Empty property report\n"
-"\n"
-"Dear Empty Property Officer,\n"
-"\n"
-"This is a new referral of an empty property in your area made by a user of "
-"the\n"
-"website ReportEmptyHomes.com; the website user has been told that the case "
-"has\n"
-"been referred to you. We would be grateful if you could do whatever you can "
-"to\n"
-"help get this property back into use. We will contact the user in a month "
-"and\n"
-"again in six months and ask them what has happened to the property. \n"
-"\n"
-"We'd encourage you to tell us what you have done, and when the property "
-"comes\n"
-"back into use, by filling in an update against the property referral on the\n"
-"website:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"This gives useful feedback to website users and helps them understand what\n"
-"action you are taking. We will offer advice to the user on other action "
-"they\n"
-"might take if the property isn't successfully dealt with. \n"
-"\n"
-"<?=$values['has_photo']?>If you would like help or advice on getting empty "
-"properties back into use\n"
-"there is lots of useful information on the Empty Homes Agency's website\n"
-"www.EmptyHomes.com - if you have further questions please give us a call. \n"
-"\n"
-"----------\n"
-"\n"
-"Name: <?=$values['name']?>\n"
-"\n"
-"Email: <?=$values['email']?>\n"
-"\n"
-"<?=$values['phone_line']?>Subject: <?=$values['title']?>\n"
-"\n"
-"Property type: <?=$values['category']?>\n"
-"\n"
-"Details: <?=$values['detail']?>\n"
-"\n"
-"<?=$values['closest_address']?>----------\n"
-"\n"
-"Replies to this email will go to the user who submitted the report, if\n"
-"you would like to ask for any further information.\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-msgstr ""
-
-#: templates/emails/tms-confirm
-msgid ""
-"Subject: Confirm your expression of interest in TextMyStreet\n"
-"\n"
-"Hi,\n"
-"\n"
-"Please click on the link below to confirm your expression of\n"
-"interest in TextMyStreet:\n"
-"\n"
-" <?=$values['url']?>\n"
-"\n"
-"If you can't click the link, please copy and paste it to the\n"
-"address bar of your web browser.\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-msgstr ""
-
-#: templates/emails/update-confirm
-msgid ""
-"Subject: Confirm your update on FixMyStreet\n"
-"\n"
-"Hi <?=$values['name']?>,\n"
-"\n"
-"Please click on the link below to confirm the update you just wrote:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"If you can't click the link, please copy and paste it to the\n"
-"address bar of your web browser.\n"
-"\n"
-"Your update reads:\n"
-"\n"
-"<?=$values['update']?>\n"
-"\n"
-"Yours, \n"
-"The FixMyStreet team\n"
-msgstr ""
-"Subject: Bekreft din oppdatering på FiksGataMi\n"
-"\n"
-"Hei <?=$values['name']?>,\n"
-"\n"
-"Vennligst klikk på lenken under for å bekrefte oppdateringen du skrev "
-"nettopp:\n"
-"\n"
-"<?=$values['url']?>\n"
-"\n"
-"Hvis du ikke kan klikke på lenken, kopier og lim den inn\n"
-"i adressefeltet på din nettleser..\n"
-"\n"
-"Din oppdatering inneholder:\n"
-"\n"
-"<?=$values['update']?>\n"
-"\n"
-"Vennlig hilsen, \n"
-"FiksGataMi-gruppen\n"
-
-#: templates/emails/emptyhomes/alert-problem
-msgid ""
-"Subject: New empty properties on reportemptyhomes.com\n"
-"\n"
-"The following empty properties have been added:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are new properties,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/emptyhomes/alert-problem-area
-msgid ""
-"Subject: New empty properties in <?=$values['area_name']?> on "
-"reportemptyhomes.com\n"
-"\n"
-"The following empty properties have been added within\n"
-"<?=$values['area_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are new properties in\n"
-"<?=$values['area_name']?>, please follow this link: \n"
-"<?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/emptyhomes/alert-problem-council
-msgid ""
-"Subject: New empty properties reported to <?=$values['area_name']?> on "
-"reportemptyhomes.com\n"
-"\n"
-"The following empty properties have been reported to <?=$values['area_name']?"
-">:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are new properties reported to\n"
-"<?=$values['area_name']?>, please follow this link: \n"
-"<?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/emptyhomes/alert-problem-nearby
-msgid ""
-"Subject: New nearby empty properties on reportemptyhomes.com\n"
-"\n"
-"The following nearby empty properties have been added:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are nearby properties,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/emptyhomes/alert-problem-ward
-msgid ""
-"Subject: New empty properties reported to <?=$values['area_name']?> within <?"
-"=$values['ward_name']?> on reportemptyhomes.com\n"
-"\n"
-"The following empty properties have been reported to <?=$values['area_name']?"
-">\n"
-"within <?=$values['ward_name']?>:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are new properties reported to\n"
-"<?=$values['area_name']?> within <?=$values['ward_name']?>,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
-
-#: templates/emails/emptyhomes/alert-update
-msgid ""
-"Subject: New updates on empty property- '<?=$values['title']?>'\n"
-"\n"
-"The following updates have been left on this empty property:\n"
-"\n"
-"<?=$values['data']?>\n"
-"\n"
-"View or reply to these updates: <?=$values['problem_url']?>\n"
-"\n"
-"Yours, \n"
-"reportemptyhomes.com\n"
-"\n"
-"To stop receiving emails when there are new updates on this property,\n"
-"please follow this link: <?=$values['unsubscribe_url']?>\n"
-msgstr ""
diff --git a/notes/INSTALL b/notes/INSTALL
index fcb1f5579..2a1dba635 100644
--- a/notes/INSTALL
+++ b/notes/INSTALL
@@ -39,7 +39,7 @@ sudo apt-get install `cat conf/packages`
# also install these that are available by default on mySoc boxes:
sudo apt-get install \
- libregexp-common-perl php5-cli perl-doc libtest-exception-perl
+ libregexp-common-perl php5-cli perl-doc libtest-exception-perl locales-all
# SETTING UP APACHE
diff --git a/notes/INSTALL-catalyst.txt b/notes/INSTALL-catalyst.txt
new file mode 100644
index 000000000..1c44bea80
--- /dev/null
+++ b/notes/INSTALL-catalyst.txt
@@ -0,0 +1,108 @@
+These are notes on how to get the Catalyst version of FMS up and running during the change from Apache/CGI based code to pure Catalyst based code.
+
+########## location ############
+
+Pretty much everything that follows assumes that you run the commands from the root of the fixmystreet project.
+
+
+########## setup environment ###########
+
+There is a little script that is used to setup the correct environment needed for FMS to run (perl library paths, cpan install locations, bin paths etc). It can either be evaled in a bash shell to set environment variables:
+
+ eval `./setenv.pl`
+
+or used as a runner to start scripts (eg in cron):
+
+ setenv.pl some_script
+
+
+########## build CPAN dependecies ###########
+
+There are many CPAN dependencies that should be dealt with using module-manage.pl which takes care of fetching specific versions of packages from CPAN and building them. To install all the CPAN packages needed:
+
+ eval `./setenv.pl`
+ module-manage.pl setup
+
+Look in the perl-external directory for details. Notably the following are important:
+
+urls.txt - url to the specific packages to fetch
+modules.txt - list of all modules that need to be built
+minicpan/ - local subset of cpan - used as source for all packages
+local-lib - where the cpan modules get built to
+lib - some initial modules needed for bootstrap
+bin - scripts to make it all work
+
+Read the perl-external/bin/module-manage.pl code to see how it all works. It is basically a wrapper around cpanm (which builds the packages) and dpan (which helps maintain the fake cpan subset).
+
+If you need to add a module do it using:
+
+ module-manage.pl add Module::To::Add
+
+and it will update all the relevant bits.
+
+If a module won't build (Test::WWW::Mechanize and HTTP::Server::Simple fail tests for me but the failures are not pertinent) then the module-manage script will bail out. Look in ~/.cpanm/build_log to see what went wrong. You can force an install if the test failures are not important by running cpanm directly:
+
+ cpanm \
+ --mirror /absolute/path/to/perl-external/minicpan \
+ --mirror-only \
+ --force \
+ Test::WWW::Mechanize
+
+Hopefully once it is all built we can put something like this into the exec in vhosts.pl to make sure that the setup is current:
+
+ setenv.pl module-manage.pl setup
+
+
+Note: Others are starting to work on this and it might be a good idea to switch
+to their output:
+http://blogs.perl.org/users/sebastian_willert/2011/03/how-i-distribute-my-projects.html
+
+
+############ running the code (dev server) ############
+
+Start the catalyst dev server using:
+
+ CATALYST_DEBUG=1 ./script/fixmystreet_app_server.pl -r
+
+CATALYST_DEBUG turns on the very verbose debug output which is helpful to see what the code is actually doing. The '-r' flag watches for files to change and then restarts the dev server.
+
+
+############ running the code (under Apache) #############
+
+The Catalyst App (FixMyStreet::App) is the last thing that gets tried by apache if none of the redirects in httpd.conf get triggered. This means that the existing code can run unchanged and the bits we want to have Catalyst process just get removed from the redirects.
+
+Currently the httpd.conf runs the FixMyStreet::App as a cgi (rather than fastcgi) which means that the startup hit happens everytime. In production this should probably be fastcgi, or we could go all hip and use the psgi interface. Whatevah!
+
+
+################ databases ######################
+
+The database modifications needed are in the scripts 'db/schema_00*' which should be applied in order.
+
+After further changes to the schema the script 'db/rerun_dbic_loader.pl' should be run to inspect the database and update the model files in perllib/FixMyStreet/DB/Result. Also note that several tables are ignored in that script but just deleting them from the ignore list will cause code to be generated for them.
+
+The DBIx::Class code is pulled into the FixMyStreet::App using FixMyStreet::App::Model::DB which is just a thin wrapper. The separation is there so that the models can be easily used outside of Catalyst (eg in the utility scripts).
+
+The DBIx::Class code uses a separate DBI connection than the mySociety::DBHandler code. This is due to different assumptions about the transaction commit policy.
+
+
+########## What's where in the code? ##############
+
+FixMyStreet::App is a fairly standard Catalyst app, there aren't any really big surprises.
+
+Note that the FixMyStreet.pm file is used though to abstract some config related things. Note the FixMyStreet->test_mode(1) which will do things like send all emails to a memory queue for the test scripts. test_mode should only be used in test scripts, and so is different from setting STAGING to true.
+
+
+############## testing ##################
+
+There are several tests in the t directory - organized into subdirs. Note that there is a module FixMyStreet::TestMech that abstracts some things like logging in as a user and grabbing all the form error messages. This makes testing much slicker and less fiddly.
+
+Run all the tests using:
+
+ prove -lr t
+
+or a specific test in verbose mode using:
+
+ prove -lv t/app/controller/report_new.t
+
+For all the lovely options do 'prove --help'. Note I've made no attempt to make the tests be able to run in parallel, the database fiddling would not be worth it.
+The tests currently assume MAPIT_URL is set to the UK version.
diff --git a/notes/catalyst-master-merge-todos.txt b/notes/catalyst-master-merge-todos.txt
new file mode 100644
index 000000000..200fda0a5
--- /dev/null
+++ b/notes/catalyst-master-merge-todos.txt
@@ -0,0 +1,9 @@
+display_map needs a properly faked $q so it can choose a cobrand. Or should it be ported to use new cobrand?
+
+change uri_for to use cobrand->url so we don't have to
+
+should we ditch flickr import? (does not seem to be getting huge usage and those using it would probably report using another method: http://www.flickr.com/search/?w=all&q=fixmystreet&m=tags)
+
+move all FAQ over to TT2
+
+check that using specific 404 and 410 pages is ok for reports
diff --git a/notes/location_related_flow.txt b/notes/location_related_flow.txt
new file mode 100644
index 000000000..5af3c88f0
--- /dev/null
+++ b/notes/location_related_flow.txt
@@ -0,0 +1,23 @@
+--- '/' ---
+
+ Homepage has a search form that submits to /around. There is no location
+ processing done on the home page. Any queries sent to the homepage are
+ redirected to /around.
+
+--- '/around' ---
+
+ SEARCH: allows user to search for a location using postcode or other text.
+ If nothing matched error shown. If multiple match show alternatives.
+
+ LIST: If a search could be resolved to a lat/lon, or a lot/lon was in query
+ show matches near that location.
+
+ PARTIAL: If there is a partial token show a message when searching. When a
+ match is found redirect to '/report/new' for the partial to be completed.
+
+--- '/report/new' ---
+
+ Requires a lat/lng, or a tile click, or a partial report with a location
+ stored. If no location can be deteremined redirects back to '/around'. All
+ form information is lost but the partial token is preserved.
+
diff --git a/perl-external/.gitignore b/perl-external/.gitignore
new file mode 100644
index 000000000..8db64e38e
--- /dev/null
+++ b/perl-external/.gitignore
@@ -0,0 +1 @@
+local-lib
diff --git a/perl-external/bin/cpanm b/perl-external/bin/cpanm
new file mode 100755
index 000000000..15996e72c
--- /dev/null
+++ b/perl-external/bin/cpanm
@@ -0,0 +1,5748 @@
+#!/usr/bin/perl
+
+eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
+ if 0; # not running under some shell
+#
+# You want to install cpanminus? Run the following command and it will
+# install itself for you. You might want to run it as a root with sudo
+# if you want to install to places like /usr/local/bin.
+#
+# % curl -L http://cpanmin.us | perl - --self-upgrade
+#
+# If you don't have curl but wget, replace `curl -L` with `wget -O -`.
+#
+# For more details about this program, visit http://search.cpan.org/dist/App-cpanminus
+#
+# DO NOT EDIT -- this is an auto generated file
+# This chunk of stuff was generated by App::FatPacker. To find the original
+# file's code, look for the end of this BEGIN block or the string 'FATPACK'
+BEGIN {
+my %fatpacked;
+
+$fatpacked{"App/cpanminus.pm"} = <<'APP_CPANMINUS';
+ package App::cpanminus;
+ our $VERSION = "1.4007";
+
+ =head1 NAME
+
+ App::cpanminus - get, unpack, build and install modules from CPAN
+
+ =head1 SYNOPSIS
+
+ cpanm Module
+
+ Run C<cpanm -h> for more options.
+
+ =head1 DESCRIPTION
+
+ cpanminus is a script to get, unpack, build and install modules from
+ CPAN and does nothing else.
+
+ It's dependency free (can bootstrap itself), requires zero
+ configuration, and stands alone. When running, it requires only 10MB
+ of RAM.
+
+ =head1 INSTALLATION
+
+ There are several ways to install cpanminus to your system.
+
+ =head2 Package management system
+
+ There are Debian packages, RPMs, FreeBSD ports, and packages for other
+ operation systems available. If you want to use the package management system,
+ search for cpanminus and use the appropriate command to install. This makes it
+ easy to install C<cpanm> to your system without thinking about where to
+ install, and later upgrade.
+
+ =head2 Installing to system perl
+
+ You can also use the latest cpanminus to install cpanminus itself:
+
+ curl -L http://cpanmin.us | perl - --sudo App::cpanminus
+
+ This will install C<cpanm> to your bin directory like
+ C</usr/local/bin> (unless you configured C<INSTALL_BASE> with
+ L<local::lib>), so you probably need the C<--sudo> option.
+
+ =head2 Installing to local perl (perlbrew)
+
+ If you have perl in your home directory, which is the case if you use
+ tools like L<perlbrew>, you don't need the C<--sudo> option, since
+ you're most likely to have a write permission to the perl's library
+ path. You can just do:
+
+ curl -L http://cpanmin.us | perl - App::cpanminus
+
+ to install the C<cpanm> executable to the perl's bin path, like
+ C<~/perl5/perlbrew/bin/cpanm>.
+
+ =head2 Downloading the standalone executable
+
+ You can also copy the standalone executable to whatever location you'd like.
+
+ cd ~/bin
+ curl -LO http://xrl.us/cpanm
+ chmod +x cpanm
+ # edit shebang if you don't have /usr/bin/env
+
+ This just works, but be sure to grab the new version manually when you
+ upgrade because C<--self-upgrade> might not work for this.
+
+ =head1 DEPENDENCIES
+
+ perl 5.8 or later.
+
+ =over 4
+
+ =item *
+
+ 'tar' executable (bsdtar or GNU tar version 1.22 are rcommended) or Archive::Tar to unpack files.
+
+ =item *
+
+ C compiler, if you want to build XS modules.
+
+ =item *
+
+ make
+
+ =item *
+
+ Module::Build (core in 5.10)
+
+ =back
+
+ =head1 QUESTIONS
+
+ =head2 Another CPAN installer?
+
+ OK, the first motivation was this: the CPAN shell runs out of memory (or swaps
+ heavily and gets really slow) on Slicehost/linode's most affordable plan with
+ only 256MB RAM. Should I pay more to install perl modules from CPAN? I don't
+ think so.
+
+ =head2 But why a new client?
+
+ First of all, let me be clear that CPAN and CPANPLUS are great tools
+ I've used for I<literally> years (you know how many modules I have on
+ CPAN, right?). I really respect their efforts of maintaining the most
+ important tools in the CPAN toolchain ecosystem.
+
+ However, for less experienced users (mostly from outside the Perl community),
+ or even really experienced Perl developers who know how to shoot themselves in
+ their feet, setting up the CPAN toolchain often feels like yak shaving,
+ especially when all they want to do is just install some modules and start
+ writing code.
+
+ =head2 Zero-conf? How does this module get/parse/update the CPAN index?
+
+ It queries the CPAN Meta DB site running on Google AppEngine at
+ L<http://cpanmetadb.appspot.com/>. The site is updated every hour to reflect
+ the latest changes from fast syncing mirrors. The script then also falls back
+ to scrape the site L<http://search.cpan.org/>.
+
+ Fetched files are unpacked in C<~/.cpanm> and automatically cleaned up
+ periodically. You can configure the location of this with the
+ C<PERL_CPANM_HOME> environment variable.
+
+ =head2 Where does this install modules to? Do I need root access?
+
+ It installs to wherever ExtUtils::MakeMaker and Module::Build are
+ configured to (via C<PERL_MM_OPT> and C<PERL_MB_OPT>). So if you're
+ using local::lib, then it installs to your local perl5
+ directory. Otherwise it installs to the site_perl directory that
+ belongs to your perl.
+
+ cpanminus at a boot time checks whether you have configured
+ local::lib, or have the permission to install modules to the site_perl
+ directory. If neither, it automatically sets up local::lib compatible
+ installation path in a C<perl5> directory under your home
+ directory. To avoid this, run the script as the root user, with
+ C<--sudo> option or with C<--local-lib> option.
+
+ =head2 cpanminus can't install the module XYZ. Is it a bug?
+
+ It is more likely a problem with the distribution itself. cpanminus
+ doesn't support or is known to have issues with distributions like as
+ follows:
+
+ =over 4
+
+ =item *
+
+ Tests that require input from STDIN.
+
+ =item *
+
+ Tests that might fail when C<AUTOMATED_TESTING> is enabled.
+
+ =item *
+
+ Modules that have invalid numeric values as VERSION (such as C<1.1a>)
+
+ =back
+
+ These failures can be reported back to the author of the module so
+ that they can fix it accordingly, rather than me.
+
+ =head2 Does cpanm support the feature XYZ of L<CPAN> and L<CPANPLUS>?
+
+ Most likely not. Here are the things that cpanm doesn't do by
+ itself. And it's a feature - you got that from the name I<minus>,
+ right?
+
+ If you need these features, use L<CPAN>, L<CPANPLUS> or the standalone
+ tools that are mentioned.
+
+ =over 4
+
+ =item *
+
+ Bundle:: module dependencies
+
+ =item *
+
+ CPAN testers reporting
+
+ =item *
+
+ Building RPM packages from CPAN modules
+
+ =item *
+
+ Listing the outdated modules that needs upgrading. See L<cpan-outdated>
+
+ =item *
+
+ Uninstalling modules. See L<pm-uninstall>.
+
+ =item *
+
+ Showing the changes of the modules you're about to upgrade. See L<cpan-listchanges>
+
+ =item *
+
+ Patching CPAN modules with distroprefs.
+
+ =back
+
+ See L<cpanm> or C<cpanm -h> to see what cpanminus I<can> do :)
+
+ =head1 COPYRIGHT
+
+ Copyright 2010- Tatsuhiko Miyagawa
+
+ The standalone executable contains the following modules embedded.
+
+ =over 4
+
+ =item L<CPAN::DistnameInfo> Copyright 2003 Graham Barr
+
+ =item L<Parse::CPAN::Meta> Copyright 2006-2009 Adam Kennedy
+
+ =item L<local::lib> Copyright 2007-2009 Matt S Trout
+
+ =item L<HTTP::Tiny> Copyright 2011 Christian Hansen
+
+ =item L<Module::Metadata> Copyright 2001-2006 Ken Williams. 2010 Matt S Trout
+
+ =item L<version> Copyright 2004-2010 John Peacock
+
+ =back
+
+ =head1 LICENSE
+
+ Same as Perl.
+
+ =head1 CREDITS
+
+ =head2 CONTRIBUTORS
+
+ Patches and code improvements were contributed by:
+
+ Goro Fuji, Kazuhiro Osawa, Tokuhiro Matsuno, Kenichi Ishigaki, Ian
+ Wells, Pedro Melo, Masayoshi Sekimura, Matt S Trout, squeeky, horus
+ and Ingy dot Net.
+
+ =head2 ACKNOWLEDGEMENTS
+
+ Bug reports, suggestions and feedbacks were sent by, or general
+ acknowledgement goes to:
+
+ Jesse Vincent, David Golden, Andreas Koenig, Jos Boumans, Chris
+ Williams, Adam Kennedy, Audrey Tang, J. Shirley, Chris Prather, Jesse
+ Luehrs, Marcus Ramberg, Shawn M Moore, chocolateboy, Chirs Nehren,
+ Jonathan Rockway, Leon Brocard, Simon Elliott, Ricardo Signes, AEvar
+ Arnfjord Bjarmason, Eric Wilhelm, Florian Ragwitz and xaicron.
+
+ =head1 COMMUNITY
+
+ =over 4
+
+ =item L<http://github.com/miyagawa/cpanminus> - source code repository, issue tracker
+
+ =item L<irc://irc.perl.org/#toolchain> - discussions about Perl toolchain. I'm there.
+
+ =back
+
+ =head1 NO WARRANTY
+
+ This software is provided "as-is," without any express or implied
+ warranty. In no event shall the author be held liable for any damages
+ arising from the use of the software.
+
+ =head1 SEE ALSO
+
+ L<CPAN> L<CPANPLUS> L<pip>
+
+ =cut
+
+ 1;
+APP_CPANMINUS
+
+$fatpacked{"App/cpanminus/script.pm"} = <<'APP_CPANMINUS_SCRIPT';
+ package App::cpanminus::script;
+ use strict;
+ use Config;
+ use Cwd ();
+ use File::Basename ();
+ use File::Find ();
+ use File::Path ();
+ use File::Spec ();
+ use File::Copy ();
+ use Getopt::Long ();
+ use Parse::CPAN::Meta;
+ use Symbol ();
+
+ use constant WIN32 => $^O eq 'MSWin32';
+ use constant SUNOS => $^O eq 'solaris';
+
+ our $VERSION = "1.4007";
+
+ my $quote = WIN32 ? q/"/ : q/'/;
+
+ sub new {
+ my $class = shift;
+
+ bless {
+ home => "$ENV{HOME}/.cpanm",
+ cmd => 'install',
+ seen => {},
+ notest => undef,
+ installdeps => undef,
+ force => undef,
+ sudo => undef,
+ make => undef,
+ verbose => undef,
+ quiet => undef,
+ interactive => undef,
+ log => undef,
+ mirrors => [],
+ mirror_only => undef,
+ perl => $^X,
+ argv => [],
+ local_lib => undef,
+ self_contained => undef,
+ prompt_timeout => 0,
+ prompt => undef,
+ configure_timeout => 60,
+ try_lwp => 1,
+ try_wget => 1,
+ try_curl => 1,
+ uninstall_shadows => ($] < 5.012),
+ skip_installed => 1,
+ auto_cleanup => 7, # days
+ pod2man => 1,
+ installed_dists => 0,
+ scandeps => 0,
+ scandeps_tree => [],
+ format => 'tree',
+ save_dists => undef,
+ @_,
+ }, $class;
+ }
+
+ sub env {
+ my($self, $key) = @_;
+ $ENV{"PERL_CPANM_" . $key};
+ }
+
+ sub parse_options {
+ my $self = shift;
+
+ local @ARGV = @{$self->{argv}};
+ push @ARGV, split /\s+/, $self->env('OPT');
+ push @ARGV, @_;
+
+ Getopt::Long::Configure("bundling");
+ Getopt::Long::GetOptions(
+ 'f|force' => sub { $self->{skip_installed} = 0; $self->{force} = 1 },
+ 'n|notest!' => \$self->{notest},
+ 'S|sudo!' => \$self->{sudo},
+ 'v|verbose' => sub { $self->{verbose} = $self->{interactive} = 1 },
+ 'q|quiet' => \$self->{quiet},
+ 'h|help' => sub { $self->{action} = 'show_help' },
+ 'V|version' => sub { $self->{action} = 'show_version' },
+ 'perl=s' => \$self->{perl},
+ 'l|local-lib=s' => sub { $self->{local_lib} = $self->maybe_abs($_[1]) },
+ 'L|local-lib-contained=s' => sub {
+ $self->{local_lib} = $self->maybe_abs($_[1]);
+ $self->{self_contained} = 1;
+ $self->{pod2man} = undef;
+ },
+ 'mirror=s@' => $self->{mirrors},
+ 'mirror-only!' => \$self->{mirror_only},
+ 'prompt!' => \$self->{prompt},
+ 'installdeps' => \$self->{installdeps},
+ 'skip-installed!' => \$self->{skip_installed},
+ 'reinstall' => sub { $self->{skip_installed} = 0 },
+ 'interactive!' => \$self->{interactive},
+ 'i|install' => sub { $self->{cmd} = 'install' },
+ 'info' => sub { $self->{cmd} = 'info' },
+ 'look' => sub { $self->{cmd} = 'look'; $self->{skip_installed} = 0 },
+ 'self-upgrade' => sub { $self->{cmd} = 'install'; $self->{skip_installed} = 1; push @ARGV, 'App::cpanminus' },
+ 'uninst-shadows!' => \$self->{uninstall_shadows},
+ 'lwp!' => \$self->{try_lwp},
+ 'wget!' => \$self->{try_wget},
+ 'curl!' => \$self->{try_curl},
+ 'auto-cleanup=s' => \$self->{auto_cleanup},
+ 'man-pages!' => \$self->{pod2man},
+ 'scandeps' => \$self->{scandeps},
+ 'format=s' => \$self->{format},
+ 'save-dists=s' => sub {
+ $self->{save_dists} = $self->maybe_abs($_[1]);
+ },
+ );
+
+ if (!@ARGV && $0 ne '-' && !-t STDIN){ # e.g. # cpanm < author/requires.cpanm
+ push @ARGV, $self->load_argv_from_fh(\*STDIN);
+ $self->{load_from_stdin} = 1;
+ }
+
+ $self->{argv} = \@ARGV;
+ }
+
+ sub check_libs {
+ my $self = shift;
+ return if $self->{_checked}++;
+
+ $self->bootstrap_local_lib;
+ if (@{$self->{bootstrap_deps} || []}) {
+ local $self->{notest} = 1; # test failure in bootstrap should be tolerated
+ local $self->{scandeps} = 0;
+ $self->install_deps(Cwd::cwd, 0, @{$self->{bootstrap_deps}});
+ }
+ }
+
+ sub doit {
+ my $self = shift;
+
+ $self->setup_home;
+ $self->init_tools;
+
+ if (my $action = $self->{action}) {
+ $self->$action() and return 1;
+ }
+
+ $self->show_help(1)
+ unless @{$self->{argv}} or $self->{load_from_stdin};
+
+ $self->configure_mirrors;
+
+ my @fail;
+ for my $module (@{$self->{argv}}) {
+ if ($module =~ s/\.pm$//i) {
+ my ($volume, $dirs, $file) = File::Spec->splitpath($module);
+ $module = join '::', grep { $_ } File::Spec->splitdir($dirs), $file;
+ }
+ $self->install_module($module, 0)
+ or push @fail, $module;
+ }
+
+ if ($self->{base} && $self->{auto_cleanup}) {
+ $self->cleanup_workdirs;
+ }
+
+ if ($self->{installed_dists}) {
+ my $dists = $self->{installed_dists} > 1 ? "distributions" : "distribution";
+ $self->diag("$self->{installed_dists} $dists installed\n", 1);
+ }
+
+ if ($self->{scandeps}) {
+ $self->dump_scandeps();
+ }
+
+ return !@fail;
+ }
+
+ sub setup_home {
+ my $self = shift;
+
+ $self->{home} = $self->env('HOME') if $self->env('HOME');
+
+ unless (_writable($self->{home})) {
+ die "Can't write to cpanm home '$self->{home}': You should fix it with chown/chmod first.\n";
+ }
+
+ $self->{base} = "$self->{home}/work/" . time . ".$$";
+ File::Path::mkpath([ $self->{base} ], 0, 0777);
+
+ my $link = "$self->{home}/latest-build";
+ eval { unlink $link; symlink $self->{base}, $link };
+
+ $self->{log} = File::Spec->catfile($self->{home}, "build.log"); # because we use shell redirect
+
+ {
+ my $log = $self->{log}; my $base = $self->{base};
+ $self->{at_exit} = sub {
+ my $self = shift;
+ File::Copy::copy($self->{log}, "$self->{base}/build.log");
+ };
+ }
+
+ { open my $out, ">$self->{log}" or die "$self->{log}: $!" }
+
+ $self->chat("cpanm (App::cpanminus) $VERSION on perl $] built for $Config{archname}\n" .
+ "Work directory is $self->{base}\n");
+ }
+
+ sub fetch_meta_sco {
+ my($self, $dist) = @_;
+ return if $self->{mirror_only};
+
+ my $meta_yml = $self->get("http://search.cpan.org/meta/$dist->{distvname}/META.yml");
+ return $self->parse_meta_string($meta_yml);
+ }
+
+ sub package_index_for {
+ my ($self, $mirror) = @_;
+ return $self->source_for($mirror) . "/02packages.details.txt";
+ }
+
+ sub generate_mirror_index {
+ my ($self, $mirror) = @_;
+ my $file = $self->package_index_for($mirror);
+ my $gz_file = $file . '.gz';
+ my $index_mtime = (stat $gz_file)[9];
+
+ unless (-e $file && (stat $file)[9] >= $index_mtime) {
+ $self->chat("Uncompressing index file...\n");
+ if (eval {require Compress::Zlib}) {
+ my $gz = Compress::Zlib::gzopen($gz_file, "rb")
+ or do { $self->diag_fail("$Compress::Zlib::gzerrno opening compressed index"); return};
+ open my $fh, '>', $file
+ or do { $self->diag_fail("$! opening uncompressed index for write"); return };
+ my $buffer;
+ while (my $status = $gz->gzread($buffer)) {
+ if ($status < 0) {
+ $self->diag_fail($gz->gzerror . " reading compressed index");
+ return;
+ }
+ print $fh $buffer;
+ }
+ } else {
+ if (system("gunzip -c $gz_file > $file")) {
+ $self->diag_fail("Cannot uncompress -- please install gunzip or Compress::Zlib");
+ return;
+ }
+ }
+ utime $index_mtime, $index_mtime, $file;
+ }
+ return 1;
+ }
+
+ sub search_mirror_index {
+ my ($self, $mirror, $module) = @_;
+
+ open my $fh, '<', $self->package_index_for($mirror) or return;
+ while (<$fh>) {
+ if (m!^\Q$module\E\s+([\w\.]+)\s+(.*)!m) {
+ return $self->cpan_module($module, $2, $1);
+ }
+ }
+
+ return;
+ }
+
+ sub search_module {
+ my($self, $module) = @_;
+
+ unless ($self->{mirror_only}) {
+ $self->chat("Searching $module on cpanmetadb ...\n");
+ my $uri = "http://cpanmetadb.appspot.com/v1.0/package/$module";
+ my $yaml = $self->get($uri);
+ my $meta = $self->parse_meta_string($yaml);
+ if ($meta && $meta->{distfile}) {
+ return $self->cpan_module($module, $meta->{distfile}, $meta->{version});
+ }
+
+ $self->diag_fail("Finding $module on cpanmetadb failed.");
+
+ $self->chat("Searching $module on search.cpan.org ...\n");
+ my $uri = "http://search.cpan.org/perldoc?$module";
+ my $html = $self->get($uri);
+ $html =~ m!<a href="/CPAN/authors/id/(.*?\.(?:tar\.gz|tgz|tar\.bz2|zip))">!
+ and return $self->cpan_module($module, $1);
+
+ $self->diag_fail("Finding $module on search.cpan.org failed.");
+ }
+
+ MIRROR: for my $mirror (@{ $self->{mirrors} }) {
+ $self->chat("Searching $module on mirror $mirror ...\n");
+ my $name = '02packages.details.txt.gz';
+ my $uri = "$mirror/modules/$name";
+ my $gz_file = $self->package_index_for($mirror) . '.gz';
+
+ unless ($self->{pkgs}{$uri}) {
+ $self->chat("Downloading index file $uri ...\n");
+ $self->mirror($uri, $gz_file);
+ $self->generate_mirror_index($mirror) or next MIRROR;
+ $self->{pkgs}{$uri} = "!!retrieved!!";
+ }
+
+ my $pkg = $self->search_mirror_index($mirror, $module);
+ return $pkg if $pkg;
+
+ $self->diag_fail("Finding $module on mirror $mirror failed.");
+ }
+
+ return;
+ }
+
+ sub source_for {
+ my($self, $mirror) = @_;
+ $mirror =~ s/[^\w\.\-]+/%/g;
+
+ my $dir = "$self->{home}/sources/$mirror";
+ File::Path::mkpath([ $dir ], 0, 0777);
+
+ return $dir;
+ }
+
+ sub load_argv_from_fh {
+ my($self, $fh) = @_;
+
+ my @argv;
+ while(defined(my $line = <$fh>)){
+ chomp $line;
+ $line =~ s/#.+$//; # comment
+ $line =~ s/^\s+//; # trim spaces
+ $line =~ s/\s+$//; # trim spaces
+
+ push @argv, split ' ', $line if $line;
+ }
+ return @argv;
+ }
+
+ sub show_version {
+ print "cpanm (App::cpanminus) version $VERSION\n";
+ return 1;
+ }
+
+ sub show_help {
+ my $self = shift;
+
+ if ($_[0]) {
+ die <<USAGE;
+ Usage: cpanm [options] Module [...]
+
+ Try `cpanm --help` or `man cpanm` for more options.
+ USAGE
+ }
+
+ print <<HELP;
+ Usage: cpanm [options] Module [...]
+
+ Options:
+ -v,--verbose Turns on chatty output
+ -q,--quiet Turns off the most output
+ --interactive Turns on interactive configure (required for Task:: modules)
+ -f,--force force install
+ -n,--notest Do not run unit tests
+ -S,--sudo sudo to run install commands
+ --installdeps Only install dependencies
+ --reinstall Reinstall the distribution even if you already have the latest version installed
+ --mirror Specify the base URL for the mirror (e.g. http://cpan.cpantesters.org/)
+ --mirror-only Use the mirror's index file instead of the CPAN Meta DB
+ --prompt Prompt when configure/build/test fails
+ -l,--local-lib Specify the install base to install modules
+ -L,--local-lib-contained Specify the install base to install all non-core modules
+ --auto-cleanup Number of days that cpanm's work directories expire in. Defaults to 7
+
+ Commands:
+ --self-upgrade upgrades itself
+ --info Displays distribution info on CPAN
+ --look Opens the distribution with your SHELL
+ -V,--version Displays software version
+
+ Examples:
+
+ cpanm Test::More # install Test::More
+ cpanm MIYAGAWA/Plack-0.99_05.tar.gz # full distribution path
+ cpanm http://example.org/LDS/CGI.pm-3.20.tar.gz # install from URL
+ cpanm ~/dists/MyCompany-Enterprise-1.00.tar.gz # install from a local file
+ cpanm --interactive Task::Kensho # Configure interactively
+ cpanm . # install from local directory
+ cpanm --installdeps . # install all the deps for the current directory
+ cpanm -L extlib Plack # install Plack and all non-core deps into extlib
+ cpanm --mirror http://cpan.cpantesters.org/ DBI # use the fast-syncing mirror
+
+ You can also specify the default options in PERL_CPANM_OPT environment variable in the shell rc:
+
+ export PERL_CPANM_OPT="--prompt --reinstall -l ~/perl --mirror http://cpan.cpantesters.org"
+
+ Type `man cpanm` or `perldoc cpanm` for the more detailed explanation of the options.
+
+ HELP
+
+ return 1;
+ }
+
+ sub _writable {
+ my $dir = shift;
+ my @dir = File::Spec->splitdir($dir);
+ while (@dir) {
+ $dir = File::Spec->catdir(@dir);
+ if (-e $dir) {
+ return -w _;
+ }
+ pop @dir;
+ }
+
+ return;
+ }
+
+ sub maybe_abs {
+ my($self, $lib) = @_;
+ $lib =~ /^[~\/]/ ? $lib : Cwd::abs_path($lib);
+ }
+
+ sub bootstrap_local_lib {
+ my $self = shift;
+
+ # If -l is specified, use that.
+ if ($self->{local_lib}) {
+ return $self->setup_local_lib($self->{local_lib});
+ }
+
+ # root, locally-installed perl or --sudo: don't care about install_base
+ return if $self->{sudo} or (_writable($Config{installsitelib}) and _writable($Config{installsitebin}));
+
+ # local::lib is configured in the shell -- yay
+ if ($ENV{PERL_MM_OPT} and ($ENV{MODULEBUILDRC} or $ENV{PERL_MB_OPT})) {
+ $self->bootstrap_local_lib_deps;
+ return;
+ }
+
+ $self->setup_local_lib;
+
+ $self->diag(<<DIAG);
+ !
+ ! Can't write to $Config{installsitelib} and $Config{installsitebin}: Installing modules to $ENV{HOME}/perl5
+ ! To turn off this warning, you have to do one of the following:
+ ! - run me as a root or with --sudo option (to install to $Config{installsitelib} and $Config{installsitebin})
+ | - run me with --local-lib option e.g. cpanm --local-lib=~/perl5
+ ! - Set PERL_CPANM_OPT="--local-lib=~/perl5" environment variable (in your shell rc file)
+ ! - Configure local::lib in your shell to set PERL_MM_OPT etc.
+ !
+ DIAG
+ sleep 2;
+ }
+
+ sub _core_only_inc {
+ my($self, $base) = @_;
+ require local::lib;
+ (
+ local::lib->install_base_perl_path($base),
+ local::lib->install_base_arch_path($base),
+ @Config{qw(privlibexp archlibexp)},
+ );
+ }
+
+ sub _dump_inc {
+ my($self, $inc, $std_inc) = @_;
+
+ # $self->{base} for ModuleBuildPatch.pm, . for inc/Module/Install.pm
+ my @new_inc = map { qq('$_') } (@$inc, $self->{base}, '.');
+ my @exclude_inc = map { qq('$_') } grep { $_ ne '.' && !ref } $self->_diff($inc, $std_inc);
+
+ open my $out, ">$self->{base}/DumpedINC.pm" or die $!;
+ local $" = ",";
+ print $out <<EOF;
+ package DumpedINC;
+ my \%exclude = map { \$_ => 1 } (@exclude_inc);
+ sub import {
+ if (\$_[1] eq "tests") {
+ \@INC = grep !\$exclude{\$_}, \@INC;
+ } else {
+ \@INC = (@new_inc);
+ }
+ }
+ 1;
+ EOF
+ }
+
+ sub _diff {
+ my($self, $old, $new) = @_;
+
+ my @diff;
+ my %old = map { $_ => 1 } @$old;
+ for my $n (@$new) {
+ push @diff, $n unless exists $old{$n};
+ }
+
+ @diff;
+ }
+
+ sub _setup_local_lib_env {
+ my($self, $base) = @_;
+ local $SIG{__WARN__} = sub { }; # catch 'Attempting to write ...'
+ local::lib->setup_env_hash_for($base);
+ }
+
+ sub setup_local_lib {
+ my($self, $base) = @_;
+
+ require local::lib;
+ {
+ local $0 = 'cpanm'; # so curl/wget | perl works
+ $base ||= "~/perl5";
+ if ($self->{self_contained}) {
+ my @inc = $self->_core_only_inc($base);
+ $self->_dump_inc(\@inc, \@INC);
+ $self->{search_inc} = [ @inc ];
+ } else {
+ $self->{search_inc} = [
+ local::lib->install_base_arch_path($base),
+ local::lib->install_base_perl_path($base),
+ @INC,
+ ];
+ }
+ $self->_setup_local_lib_env($base);
+ }
+
+ $self->bootstrap_local_lib_deps;
+ }
+
+ sub bootstrap_local_lib_deps {
+ my $self = shift;
+ push @{$self->{bootstrap_deps}},
+ 'ExtUtils::MakeMaker' => 6.31,
+ 'ExtUtils::Install' => 1.46,
+ 'Module::Build' => 0.36;
+ }
+
+ sub prompt_bool {
+ my($self, $mess, $def) = @_;
+
+ my $val = $self->prompt($mess, $def);
+ return lc $val eq 'y';
+ }
+
+ sub prompt {
+ my($self, $mess, $def) = @_;
+
+ my $isa_tty = -t STDIN && (-t STDOUT || !(-f STDOUT || -c STDOUT)) ;
+ my $dispdef = defined $def ? "[$def] " : " ";
+ $def = defined $def ? $def : "";
+
+ if (!$self->{prompt} || (!$isa_tty && eof STDIN)) {
+ return $def;
+ }
+
+ local $|=1;
+ local $\;
+ my $ans;
+ eval {
+ local $SIG{ALRM} = sub { undef $ans; die "alarm\n" };
+ print STDOUT "$mess $dispdef";
+ alarm $self->{prompt_timeout} if $self->{prompt_timeout};
+ $ans = <STDIN>;
+ alarm 0;
+ };
+ if ( defined $ans ) {
+ chomp $ans;
+ } else { # user hit ctrl-D or alarm timeout
+ print STDOUT "\n";
+ }
+
+ return (!defined $ans || $ans eq '') ? $def : $ans;
+ }
+
+ sub diag_ok {
+ my($self, $msg) = @_;
+ chomp $msg;
+ $msg ||= "OK";
+ if ($self->{in_progress}) {
+ $self->_diag("$msg\n");
+ $self->{in_progress} = 0;
+ }
+ $self->log("-> $msg\n");
+ }
+
+ sub diag_fail {
+ my($self, $msg, $always) = @_;
+ chomp $msg;
+ if ($self->{in_progress}) {
+ $self->_diag("FAIL\n");
+ $self->{in_progress} = 0;
+ }
+
+ if ($msg) {
+ $self->_diag("! $msg\n", $always);
+ $self->log("-> FAIL $msg\n");
+ }
+ }
+
+ sub diag_progress {
+ my($self, $msg) = @_;
+ chomp $msg;
+ $self->{in_progress} = 1;
+ $self->_diag("$msg ... ");
+ $self->log("$msg\n");
+ }
+
+ sub _diag {
+ my($self, $msg, $always) = @_;
+ print STDERR $msg if $always or $self->{verbose} or !$self->{quiet};
+ }
+
+ sub diag {
+ my($self, $msg, $always) = @_;
+ $self->_diag($msg, $always);
+ $self->log($msg);
+ }
+
+ sub chat {
+ my $self = shift;
+ print STDERR @_ if $self->{verbose};
+ $self->log(@_);
+ }
+
+ sub log {
+ my $self = shift;
+ open my $out, ">>$self->{log}";
+ print $out @_;
+ }
+
+ sub run {
+ my($self, $cmd) = @_;
+
+ if (WIN32 && ref $cmd eq 'ARRAY') {
+ $cmd = join q{ }, map { $self->shell_quote($_) } @$cmd;
+ }
+
+ if (ref $cmd eq 'ARRAY') {
+ my $pid = fork;
+ if ($pid) {
+ waitpid $pid, 0;
+ return !$?;
+ } else {
+ $self->run_exec($cmd);
+ }
+ } else {
+ unless ($self->{verbose}) {
+ $cmd .= " >> " . $self->shell_quote($self->{log}) . " 2>&1";
+ }
+ !system $cmd;
+ }
+ }
+
+ sub run_exec {
+ my($self, $cmd) = @_;
+
+ if (ref $cmd eq 'ARRAY') {
+ unless ($self->{verbose}) {
+ open my $logfh, ">>", $self->{log};
+ open STDERR, '>&', $logfh;
+ open STDOUT, '>&', $logfh;
+ close $logfh;
+ }
+ exec @$cmd;
+ } else {
+ unless ($self->{verbose}) {
+ $cmd .= " >> " . $self->shell_quote($self->{log}) . " 2>&1";
+ }
+ exec $cmd;
+ }
+ }
+
+ sub run_timeout {
+ my($self, $cmd, $timeout) = @_;
+ return $self->run($cmd) if WIN32 || $self->{verbose} || !$timeout;
+
+ my $pid = fork;
+ if ($pid) {
+ eval {
+ local $SIG{ALRM} = sub { die "alarm\n" };
+ alarm $timeout;
+ waitpid $pid, 0;
+ alarm 0;
+ };
+ if ($@ && $@ eq "alarm\n") {
+ $self->diag_fail("Timed out (> ${timeout}s). Use --verbose to retry.");
+ local $SIG{TERM} = 'IGNORE';
+ kill TERM => 0;
+ waitpid $pid, 0;
+ return;
+ }
+ return !$?;
+ } elsif ($pid == 0) {
+ $self->run_exec($cmd);
+ } else {
+ $self->chat("! fork failed: falling back to system()\n");
+ $self->run($cmd);
+ }
+ }
+
+ sub configure {
+ my($self, $cmd) = @_;
+
+ # trick AutoInstall
+ local $ENV{PERL5_CPAN_IS_RUNNING} = local $ENV{PERL5_CPANPLUS_IS_RUNNING} = $$;
+
+ # e.g. skip CPAN configuration on local::lib
+ local $ENV{PERL5_CPANM_IS_RUNNING} = $$;
+
+ my $use_default = !$self->{interactive};
+ local $ENV{PERL_MM_USE_DEFAULT} = $use_default;
+
+ # skip man page generation
+ local $ENV{PERL_MM_OPT} = $ENV{PERL_MM_OPT};
+ unless ($self->{pod2man}) {
+ $ENV{PERL_MM_OPT} .= " INSTALLMAN1DIR=none INSTALLMAN3DIR=none";
+ }
+
+ local $self->{verbose} = $self->{verbose} || $self->{interactive};
+ $self->run_timeout($cmd, $self->{configure_timeout});
+ }
+
+ sub build {
+ my($self, $cmd, $distname) = @_;
+
+ return 1 if $self->run_timeout($cmd, $self->{build_timeout});
+ while (1) {
+ my $ans = lc $self->prompt("Building $distname failed.\nYou can s)kip, r)etry or l)ook ?", "s");
+ return if $ans eq 's';
+ return $self->build($cmd, $distname) if $ans eq 'r';
+ $self->look if $ans eq 'l';
+ }
+ }
+
+ sub test {
+ my($self, $cmd, $distname) = @_;
+ return 1 if $self->{notest};
+
+ # http://www.nntp.perl.org/group/perl.perl5.porters/2009/10/msg152656.html
+ local $ENV{AUTOMATED_TESTING} = 1
+ unless $self->env('NO_AUTOMATED_TESTING');
+
+ local $ENV{PERL5OPT} = "-I$self->{base} -MDumpedINC=tests"
+ if $self->{self_contained};
+
+ return 1 if $self->run_timeout($cmd, $self->{test_timeout});
+ if ($self->{force}) {
+ $self->diag_fail("Testing $distname failed but installing it anyway.");
+ return 1;
+ } else {
+ $self->diag_fail;
+ while (1) {
+ my $ans = lc $self->prompt("Testing $distname failed.\nYou can s)kip, r)etry, f)orce install or l)ook ?", "s");
+ return if $ans eq 's';
+ return $self->test($cmd, $distname) if $ans eq 'r';
+ return 1 if $ans eq 'f';
+ $self->look if $ans eq 'l';
+ }
+ }
+ }
+
+ sub install {
+ my($self, $cmd, $uninst_opts) = @_;
+
+ if ($self->{sudo}) {
+ unshift @$cmd, "sudo";
+ }
+
+ if ($self->{uninstall_shadows} && !$ENV{PERL_MM_OPT}) {
+ push @$cmd, @$uninst_opts;
+ }
+
+ $self->run($cmd);
+ }
+
+ sub look {
+ my $self = shift;
+
+ my $shell = $ENV{SHELL};
+ $shell ||= $ENV{COMSPEC} if WIN32;
+ if ($shell) {
+ my $cwd = Cwd::cwd;
+ $self->diag("Entering $cwd with $shell\n");
+ system $shell;
+ } else {
+ $self->diag_fail("You don't seem to have a SHELL :/");
+ }
+ }
+
+ sub chdir {
+ my $self = shift;
+ chdir(File::Spec->canonpath($_[0])) or die "$_[0]: $!";
+ }
+
+ sub configure_mirrors {
+ my $self = shift;
+ unless (@{$self->{mirrors}}) {
+ $self->{mirrors} = [ 'http://search.cpan.org/CPAN' ];
+ }
+ for (@{$self->{mirrors}}) {
+ s!^/!file:///!;
+ s!/$!!;
+ }
+ }
+
+ sub self_upgrade {
+ my $self = shift;
+ $self->{argv} = [ 'App::cpanminus' ];
+ return; # continue
+ }
+
+ sub install_module {
+ my($self, $module, $depth) = @_;
+
+ if ($self->{seen}{$module}++) {
+ $self->chat("Already tried $module. Skipping.\n");
+ return 1;
+ }
+
+ my $dist = $self->resolve_name($module);
+ unless ($dist) {
+ $self->diag_fail("Couldn't find module or a distribution $module", 1);
+ return;
+ }
+
+ if ($dist->{distvname} && $self->{seen}{$dist->{distvname}}++) {
+ $self->chat("Already tried $dist->{distvname}. Skipping.\n");
+ return 1;
+ }
+
+ if ($self->{cmd} eq 'info') {
+ print $self->format_dist($dist), "\n";
+ return 1;
+ }
+
+ $self->check_libs;
+ $self->setup_module_build_patch unless $self->{pod2man};
+
+ if ($dist->{module}) {
+ my($ok, $local) = $self->check_module($dist->{module}, $dist->{module_version} || 0);
+ if ($self->{skip_installed} && $ok) {
+ $self->diag("$dist->{module} is up to date. ($local)\n", 1);
+ return 1;
+ }
+ }
+
+ if ($dist->{dist} eq 'perl'){
+ $self->diag("skipping $dist->{pathname}\n");
+ return 1;
+ }
+
+ $self->diag("--> Working on $module\n");
+
+ $dist->{dir} ||= $self->fetch_module($dist);
+
+ unless ($dist->{dir}) {
+ $self->diag_fail("Failed to fetch distribution $dist->{distvname}", 1);
+ return;
+ }
+
+ $self->chat("Entering $dist->{dir}\n");
+ $self->chdir($self->{base});
+ $self->chdir($dist->{dir});
+
+ if ($self->{cmd} eq 'look') {
+ $self->look;
+ return 1;
+ }
+
+ return $self->build_stuff($module, $dist, $depth);
+ }
+
+ sub format_dist {
+ my($self, $dist) = @_;
+
+ # TODO support --dist-format?
+ return "$dist->{cpanid}/$dist->{filename}";
+ }
+
+ sub fetch_module {
+ my($self, $dist) = @_;
+
+ $self->chdir($self->{base});
+
+ for my $uri (@{$dist->{uris}}) {
+ $self->diag_progress("Fetching $uri");
+
+ # Ugh, $dist->{filename} can contain sub directory
+ my $filename = $dist->{filename} || $uri;
+ my $name = File::Basename::basename($filename);
+
+ my $cancelled;
+ my $fetch = sub {
+ my $file;
+ eval {
+ local $SIG{INT} = sub { $cancelled = 1; die "SIGINT\n" };
+ $self->mirror($uri, $name);
+ $file = $name if -e $name;
+ };
+ $self->chat("$@") if $@ && $@ ne "SIGINT\n";
+ return $file;
+ };
+
+ my($try, $file);
+ while ($try++ < 3) {
+ $file = $fetch->();
+ last if $cancelled or $file;
+ $self->diag_fail("Download $uri failed. Retrying ... ");
+ }
+
+ if ($cancelled) {
+ $self->diag_fail("Download cancelled.");
+ return;
+ }
+
+ unless ($file) {
+ $self->diag_fail("Failed to download $uri");
+ next;
+ }
+
+ $self->diag_ok;
+ $dist->{local_path} = File::Spec->rel2abs($name);
+
+ my $dir = $self->unpack($file);
+ next unless $dir; # unpack failed
+
+ if (my $save = $self->{save_dists}) {
+ my $path = "$save/authors/id/$dist->{pathname}";
+ $self->chat("Copying $name to $path\n");
+ File::Path::mkpath([ File::Basename::dirname($path) ], 0, 0777);
+ File::Copy::copy($file, $path) or warn $!;
+ }
+
+ return $dist, $dir;
+ }
+ }
+
+ sub unpack {
+ my($self, $file) = @_;
+ $self->chat("Unpacking $file\n");
+ my $dir = $file =~ /\.zip/i ? $self->unzip($file) : $self->untar($file);
+ unless ($dir) {
+ $self->diag_fail("Failed to unpack $file: no directory");
+ }
+ return $dir;
+ }
+
+ sub resolve_name {
+ my($self, $module) = @_;
+
+ # URL
+ if ($module =~ /^(ftp|https?|file):/) {
+ if ($module =~ m!authors/id/!) {
+ return $self->cpan_dist($module, $module);
+ } else {
+ return { uris => [ $module ] };
+ }
+ }
+
+ # Directory
+ if ($module =~ m!^[\./]! && -d $module) {
+ return {
+ source => 'local',
+ dir => Cwd::abs_path($module),
+ };
+ }
+
+ # File
+ if (-f $module) {
+ return {
+ source => 'local',
+ uris => [ "file://" . Cwd::abs_path($module) ],
+ };
+ }
+
+ # cpan URI
+ if ($module =~ s!^cpan:///distfile/!!) {
+ return $self->cpan_dist($module);
+ }
+
+ # PAUSEID/foo
+ if ($module =~ m!([A-Z]{3,})/!) {
+ return $self->cpan_dist($module);
+ }
+
+ # Module name
+ return $self->search_module($module);
+ }
+
+ sub cpan_module {
+ my($self, $module, $dist, $version) = @_;
+
+ my $dist = $self->cpan_dist($dist);
+ $dist->{module} = $module;
+ $dist->{module_version} = $version if $version && $version ne 'undef';
+
+ return $dist;
+ }
+
+ sub cpan_dist {
+ my($self, $dist, $url) = @_;
+
+ $dist =~ s!^([A-Z]{3})!substr($1,0,1)."/".substr($1,0,2)."/".$1!e;
+
+ require CPAN::DistnameInfo;
+ my $d = CPAN::DistnameInfo->new($dist);
+
+ if ($url) {
+ $url = [ $url ] unless ref $url eq 'ARRAY';
+ } else {
+ my $id = $d->cpanid;
+ my $fn = substr($id, 0, 1) . "/" . substr($id, 0, 2) . "/" . $id . "/" . $d->filename;
+
+ my @mirrors = @{$self->{mirrors}};
+ my @urls = map "$_/authors/id/$fn", @mirrors;
+
+ $url = \@urls,
+ }
+
+ return {
+ $d->properties,
+ source => 'cpan',
+ uris => $url,
+ };
+ }
+
+ sub setup_module_build_patch {
+ my $self = shift;
+
+ open my $out, ">$self->{base}/ModuleBuildSkipMan.pm" or die $!;
+ print $out <<EOF;
+ package ModuleBuildSkipMan;
+ CHECK {
+ if (%Module::Build::) {
+ no warnings 'redefine';
+ *Module::Build::Base::ACTION_manpages = sub {};
+ *Module::Build::Base::ACTION_docs = sub {};
+ }
+ }
+ 1;
+ EOF
+ }
+
+ sub check_module {
+ my($self, $mod, $want_ver) = @_;
+
+ require Module::Metadata;
+ my $meta = Module::Metadata->new_from_module($mod, inc => $self->{search_inc})
+ or return 0, undef;
+
+ my $version = $meta->version;
+
+ # When -L is in use, the version loaded from 'perl' library path
+ # might be newer than the version that is shipped with the current perl
+ if ($self->{self_contained} && $self->loaded_from_perl_lib($meta)) {
+ my $core_version = eval {
+ require Module::CoreList;
+ $Module::CoreList::version{$]+0}{$mod};
+ };
+
+ # HACK: Module::Build 0.3622 or later has non-core module
+ # dependencies such as Perl::OSType and CPAN::Meta, and causes
+ # issues when a newer version is loaded from 'perl' while deps
+ # are loaded from the 'site' library path. Just assume it's
+ # not in the core, and install to the new local library path.
+ if ($mod eq 'Module::Build' && $core_version != $version) {
+ return 0, undef;
+ }
+
+ $version = $core_version if $core_version;
+ }
+
+ $self->{local_versions}{$mod} = $version;
+
+ if ($self->is_deprecated($meta)){
+ return 0, $version;
+ } elsif (!$want_ver or $version >= version->new($want_ver)) {
+ return 1, ($version || 'undef');
+ } else {
+ return 0, $version;
+ }
+ }
+
+ sub is_deprecated {
+ my($self, $meta) = @_;
+
+ my $deprecated = eval {
+ require Module::CoreList;
+ Module::CoreList::is_deprecated($meta->{module});
+ };
+
+ return unless $deprecated;
+ return $self->loaded_from_perl_lib($meta);
+ }
+
+ sub loaded_from_perl_lib {
+ my($self, $meta) = @_;
+
+ require Config;
+ for my $dir (qw(archlibexp privlibexp)) {
+ my $confdir = $Config{$dir};
+ if ($confdir eq substr($meta->filename, 0, length($confdir))) {
+ return 1;
+ }
+ }
+
+ return;
+ }
+
+ sub should_install {
+ my($self, $mod, $ver) = @_;
+
+ $self->chat("Checking if you have $mod $ver ... ");
+ my($ok, $local) = $self->check_module($mod, $ver);
+
+ if ($ok) { $self->chat("Yes ($local)\n") }
+ elsif ($local) { $self->chat("No ($local < $ver)\n") }
+ else { $self->chat("No\n") }
+
+ return $mod unless $ok;
+ return;
+ }
+
+ sub install_deps {
+ my($self, $dir, $depth, @deps) = @_;
+
+ my(@install, %seen);
+ while (my($mod, $ver) = splice @deps, 0, 2) {
+ next if $seen{$mod} or $mod eq 'perl' or $mod eq 'Config';
+ if ($self->should_install($mod, $ver)) {
+ push @install, $mod;
+ $seen{$mod} = 1;
+ }
+ }
+
+ if (@install) {
+ $self->diag("==> Found dependencies: " . join(", ", @install) . "\n");
+ }
+
+ my @fail;
+ for my $mod (@install) {
+ $self->install_module($mod, $depth + 1)
+ or push @fail, $mod;
+ }
+
+ $self->chdir($self->{base});
+ $self->chdir($dir) if $dir;
+
+ return @fail;
+ }
+
+ sub install_deps_bailout {
+ my($self, $target, $dir, $depth, @deps) = @_;
+
+ my @fail = $self->install_deps($dir, $depth, @deps);
+ if (@fail) {
+ unless ($self->prompt_bool("Installing the following dependencies failed:\n==> " .
+ join(", ", @fail) . "\nDo you want to continue building $target anyway?", "n")) {
+ $self->diag_fail("Bailing out the installation for $target. Retry with --prompt or --force.", 1);
+ return;
+ }
+ }
+
+ return 1;
+ }
+
+ sub build_stuff {
+ my($self, $stuff, $dist, $depth) = @_;
+
+ my @config_deps;
+ if (!%{$dist->{meta} || {}} && -e 'META.yml') {
+ $self->chat("Checking configure dependencies from META.yml\n");
+ $dist->{meta} = $self->parse_meta('META.yml');
+ }
+
+ if (!$dist->{meta} && $dist->{source} eq 'cpan') {
+ $self->chat("META.yml not found or unparsable. Fetching META.yml from search.cpan.org\n");
+ $dist->{meta} = $self->fetch_meta_sco($dist);
+ }
+
+ $dist->{meta} ||= {};
+
+ push @config_deps, %{$dist->{meta}{configure_requires} || {}};
+
+ my $target = $dist->{meta}{name} ? "$dist->{meta}{name}-$dist->{meta}{version}" : $dist->{dir};
+
+ $self->install_deps_bailout($target, $dist->{dir}, $depth, @config_deps)
+ or return;
+
+ $self->diag_progress("Configuring $target");
+
+ my $configure_state = $self->configure_this($dist);
+
+ $self->diag_ok($configure_state->{configured_ok} ? "OK" : "N/A");
+
+ my @deps = $self->find_prereqs($dist);
+
+ my $distname = $dist->{meta}{name} ? "$dist->{meta}{name}-$dist->{meta}{version}" : $stuff;
+
+ my $walkup;
+ if ($self->{scandeps}) {
+ $walkup = $self->scandeps_append_child($dist);
+ }
+
+ $self->install_deps_bailout($distname, $dist->{dir}, $depth, @deps)
+ or return;
+
+ if ($self->{scandeps}) {
+ unless ($configure_state->{configured_ok}) {
+ my $diag = <<DIAG;
+ ! Configuring $distname failed. See $self->{log} for details.
+ ! You might have to install the following modules first to get --scandeps working correctly.
+ DIAG
+ if (@config_deps) {
+ my @tree = @{$self->{scandeps_tree}};
+ $diag .= "!\n" . join("", map "! * $_->[0]{module}\n", @tree[0..$#tree-1]) if @tree;
+ }
+ $self->diag("!\n$diag!\n", 1);
+ }
+ $walkup->();
+ return 1;
+ }
+
+ if ($self->{installdeps} && $depth == 0) {
+ $self->diag("<== Installed dependencies for $stuff. Finishing.\n");
+ return 1;
+ }
+
+ my $installed;
+ if ($configure_state->{use_module_build} && -e 'Build' && -f _) {
+ my @switches = $self->{pod2man} ? () : ("-I$self->{base}", "-MModuleBuildSkipMan");
+ $self->diag_progress("Building " . ($self->{notest} ? "" : "and testing ") . $distname);
+ $self->build([ $self->{perl}, @switches, "./Build" ], $distname) &&
+ $self->test([ $self->{perl}, "./Build", "test" ], $distname) &&
+ $self->install([ $self->{perl}, @switches, "./Build", "install" ], [ "--uninst", 1 ]) &&
+ $installed++;
+ } elsif ($self->{make} && -e 'Makefile') {
+ $self->diag_progress("Building " . ($self->{notest} ? "" : "and testing ") . $distname);
+ $self->build([ $self->{make} ], $distname) &&
+ $self->test([ $self->{make}, "test" ], $distname) &&
+ $self->install([ $self->{make}, "install" ], [ "UNINST=1" ]) &&
+ $installed++;
+ } else {
+ my $why;
+ my $configure_failed = $configure_state->{configured} && !$configure_state->{configured_ok};
+ if ($configure_failed) { $why = "Configure failed for $distname." }
+ elsif ($self->{make}) { $why = "The distribution doesn't have a proper Makefile.PL/Build.PL" }
+ else { $why = "Can't configure the distribution. You probably need to have 'make'." }
+
+ $self->diag_fail("$why See $self->{log} for details.", 1);
+ return;
+ }
+
+ if ($installed) {
+ my $local = $self->{local_versions}{$dist->{module} || ''};
+ my $version = $dist->{module_version} || $dist->{meta}{version} || $dist->{version};
+ my $reinstall = $local && ($local eq $version);
+
+ my $how = $reinstall ? "reinstalled $distname"
+ : $local ? "installed $distname (upgraded from $local)"
+ : "installed $distname" ;
+ my $msg = "Successfully $how";
+ $self->diag_ok;
+ $self->diag("$msg\n", 1);
+ $self->{installed_dists}++;
+ return 1;
+ } else {
+ my $msg = "Building $distname failed";
+ $self->diag_fail("Installing $stuff failed. See $self->{log} for details.", 1);
+ return;
+ }
+ }
+
+ sub configure_this {
+ my($self, $dist) = @_;
+
+ my @switches;
+ @switches = ("-I$self->{base}", "-MDumpedINC") if $self->{self_contained};
+ local $ENV{PERL5LIB} = '' if $self->{self_contained};
+
+ my @mb_switches = @switches;
+ unless ($self->{pod2man}) {
+ # it has to be push, so Module::Build is loaded from the adjusted path when -L is in use
+ push @mb_switches, ("-I$self->{base}", "-MModuleBuildSkipMan");
+ }
+
+ my $state = {};
+
+ my $try_eumm = sub {
+ if (-e 'Makefile.PL') {
+ $self->chat("Running Makefile.PL\n");
+ local $ENV{X_MYMETA} = 'YAML';
+
+ # NOTE: according to Devel::CheckLib, most XS modules exit
+ # with 0 even if header files are missing, to avoid receiving
+ # tons of FAIL reports in such cases. So exit code can't be
+ # trusted if it went well.
+ if ($self->configure([ $self->{perl}, @switches, "Makefile.PL" ])) {
+ $state->{configured_ok} = -e 'Makefile';
+ }
+ $state->{configured}++;
+ }
+ };
+
+ my $try_mb = sub {
+ if (-e 'Build.PL') {
+ $self->chat("Running Build.PL\n");
+ if ($self->configure([ $self->{perl}, @mb_switches, "Build.PL" ])) {
+ $state->{configured_ok} = -e 'Build' && -f _;
+ }
+ $state->{use_module_build}++;
+ $state->{configured}++;
+ }
+ };
+
+ # Module::Build deps should use MakeMaker because that causes circular deps and fail
+ # Otherwise we should prefer Build.PL
+ my %should_use_mm = map { $_ => 1 } qw( version ExtUtils-ParseXS ExtUtils-Install ExtUtils-Manifest );
+
+ my @try;
+ if ($dist->{dist} && $should_use_mm{$dist->{dist}}) {
+ @try = ($try_eumm, $try_mb);
+ } else {
+ @try = ($try_mb, $try_eumm);
+ }
+
+ for my $try (@try) {
+ $try->();
+ last if $state->{configured_ok};
+ }
+
+ unless ($state->{configured_ok}) {
+ while (1) {
+ my $ans = lc $self->prompt("Configuring $dist->{dist} failed.\nYou can s)kip, r)etry or l)ook ?", "s");
+ last if $ans eq 's';
+ return $self->configure_this($dist) if $ans eq 'r';
+ $self->look if $ans eq 'l';
+ }
+ }
+
+ return $state;
+ }
+
+ sub safe_eval {
+ my($self, $code) = @_;
+ eval $code;
+ }
+
+ sub find_prereqs {
+ my($self, $dist) = @_;
+
+ my @deps;
+
+ my $meta = $dist->{meta};
+ if (-e 'MYMETA.yml') {
+ $self->chat("Checking dependencies from MYMETA.yml ...\n");
+ my $mymeta = $self->parse_meta('MYMETA.yml');
+ if ($mymeta) {
+ @deps = $self->extract_requires($mymeta);
+ $meta->{$_} = $mymeta->{$_} for keys %$mymeta; # merge
+ }
+ } elsif (-e '_build/prereqs') {
+ $self->chat("Checking dependencies from _build/prereqs ...\n");
+ my $mymeta = do { open my $in, "_build/prereqs"; $self->safe_eval(join "", <$in>) };
+ @deps = $self->extract_requires($mymeta);
+ }
+
+ if (-e 'Makefile') {
+ $self->chat("Finding PREREQ from Makefile ...\n");
+ open my $mf, "Makefile";
+ while (<$mf>) {
+ if (/^\#\s+PREREQ_PM => {\s*(.*?)\s*}/) {
+ my @all;
+ my @pairs = split ', ', $1;
+ for (@pairs) {
+ my ($pkg, $v) = split '=>', $_;
+ push @all, [ $pkg, $v ];
+ }
+ my $list = join ", ", map { "'$_->[0]' => $_->[1]" } @all;
+ my $prereq = $self->safe_eval("no strict; +{ $list }");
+ push @deps, %$prereq if $prereq;
+ last;
+ }
+ }
+ }
+
+ if ($dist->{module} =~ /^Bundle::/i) {
+ push @deps, $self->bundle_deps($dist);
+ }
+
+ # No need to remove, but this gets in the way of signature testing :/
+ unlink $_ for qw(MYMETA.json MYMETA.yml);
+
+ return @deps;
+ }
+
+ sub bundle_deps {
+ my($self, $dist) = @_;
+
+ my @files;
+ File::Find::find({
+ wanted => sub { push @files, File::Spec->rel2abs($_) if /\.pm/i },
+ no_chdir => 1,
+ }, '.');
+
+ my @deps;
+
+ for my $file (@files) {
+ open my $pod, "<", $file or next;
+ my $in_contents;
+ while (<$pod>) {
+ if (/^=head\d\s+CONTENTS/) {
+ $in_contents = 1;
+ } elsif (/^=/) {
+ $in_contents = 0;
+ } elsif ($in_contents) {
+ /^(\S+)\s*(\S+)?/
+ and push @deps, $1, $self->maybe_version($2);
+ }
+ }
+ }
+
+ return @deps;
+ }
+
+ sub maybe_version {
+ my($self, $string) = @_;
+ return $string && $string =~ /^\.?\d/ ? $string : undef;
+ }
+
+ sub extract_requires {
+ my($self, $meta) = @_;
+
+ my @deps;
+ push @deps, %{$meta->{requires}} if $meta->{requires};
+ push @deps, %{$meta->{build_requires}} if $meta->{build_requires};
+
+ return @deps;
+ }
+
+ sub cleanup_workdirs {
+ my $self = shift;
+
+ my $expire = time - 24 * 60 * 60 * $self->{auto_cleanup};
+ my @targets;
+
+ opendir my $dh, "$self->{home}/work";
+ while (my $e = readdir $dh) {
+ next if $e !~ /^(\d+)\.\d+$/; # {UNIX time}.{PID}
+ my $time = $1;
+ if ($time < $expire) {
+ push @targets, "$self->{home}/work/$e";
+ }
+ }
+
+ if (@targets) {
+ $self->chat("Expiring ", scalar(@targets), " work directories.\n");
+ File::Path::rmtree(\@targets, 0, 0); # safe = 0, since blib usually doesn't have write bits
+ }
+ }
+
+ sub scandeps_append_child {
+ my($self, $dist) = @_;
+
+ my $new_node = [ $dist, [] ];
+
+ my $curr_node = $self->{scandeps_current} || [ undef, $self->{scandeps_tree} ];
+ push @{$curr_node->[1]}, $new_node;
+
+ $self->{scandeps_current} = $new_node;
+
+ return sub { $self->{scandeps_current} = $curr_node };
+ }
+
+ sub dump_scandeps {
+ my $self = shift;
+
+ if ($self->{format} eq 'tree') {
+ $self->walk_down(sub {
+ my($dist, $depth) = @_;
+ if ($depth == 0) {
+ print "$dist->{distvname}\n";
+ } else {
+ print " " x ($depth - 1);
+ print "\\_ $dist->{distvname}\n";
+ }
+ }, 1);
+ } elsif ($self->{format} =~ /^dists?$/) {
+ $self->walk_down(sub {
+ my($dist, $depth) = @_;
+ print $self->format_dist($dist), "\n";
+ }, 0);
+ } elsif ($self->{format} eq 'json') {
+ require JSON;
+ print JSON::encode_json($self->{scandeps_tree});
+ } elsif ($self->{format} eq 'yaml') {
+ require YAML;
+ print YAML::Dump($self->{scandeps_tree});
+ } else {
+ $self->diag("Unknown format: $self->{format}\n");
+ }
+ }
+
+ sub walk_down {
+ my($self, $cb, $pre) = @_;
+ $self->_do_walk_down($self->{scandeps_tree}, $cb, 0, $pre);
+ }
+
+ sub _do_walk_down {
+ my($self, $children, $cb, $depth, $pre) = @_;
+
+ # DFS - $pre determines when we call the callback
+ for my $node (@$children) {
+ $cb->($node->[0], $depth) if $pre;
+ $self->_do_walk_down($node->[1], $cb, $depth + 1, $pre);
+ $cb->($node->[0], $depth) unless $pre;
+ }
+ }
+
+ sub DESTROY {
+ my $self = shift;
+ $self->{at_exit}->($self) if $self->{at_exit};
+ }
+
+ # Utils
+
+ sub shell_quote {
+ my($self, $stuff) = @_;
+ $stuff =~ /^${quote}.+${quote}$/ ? $stuff : ($quote . $stuff . $quote);
+ }
+
+ sub which {
+ my($self, $name) = @_;
+ my $exe_ext = $Config{_exe};
+ for my $dir (File::Spec->path) {
+ my $fullpath = File::Spec->catfile($dir, $name);
+ if (-x $fullpath || -x ($fullpath .= $exe_ext)) {
+ if ($fullpath =~ /\s/ && $fullpath !~ /^$quote/) {
+ $fullpath = $self->shell_quote($fullpath);
+ }
+ return $fullpath;
+ }
+ }
+ return;
+ }
+
+ sub get { $_[0]->{_backends}{get}->(@_) };
+ sub mirror { $_[0]->{_backends}{mirror}->(@_) };
+ sub untar { $_[0]->{_backends}{untar}->(@_) };
+ sub unzip { $_[0]->{_backends}{unzip}->(@_) };
+
+ sub file_get {
+ my($self, $uri) = @_;
+ open my $fh, "<$uri" or return;
+ join '', <$fh>;
+ }
+
+ sub file_mirror {
+ my($self, $uri, $path) = @_;
+ File::Copy::copy($uri, $path);
+ }
+
+ sub init_tools {
+ my $self = shift;
+
+ return if $self->{initialized}++;
+
+ if ($self->{make} = $self->which($Config{make})) {
+ $self->chat("You have make $self->{make}\n");
+ }
+
+ # use --no-lwp if they have a broken LWP, to upgrade LWP
+ if ($self->{try_lwp} && eval { require LWP::UserAgent; LWP::UserAgent->VERSION(5.802) }) {
+ $self->chat("You have LWP $LWP::VERSION\n");
+ my $ua = sub {
+ LWP::UserAgent->new(
+ parse_head => 0,
+ env_proxy => 1,
+ agent => "cpanminus/$VERSION",
+ timeout => 30,
+ @_,
+ );
+ };
+ $self->{_backends}{get} = sub {
+ my $self = shift;
+ my $res = $ua->()->request(HTTP::Request->new(GET => $_[0]));
+ return unless $res->is_success;
+ return $res->decoded_content;
+ };
+ $self->{_backends}{mirror} = sub {
+ my $self = shift;
+ my $res = $ua->()->mirror(@_);
+ $res->code;
+ };
+ } elsif ($self->{try_wget} and my $wget = $self->which('wget')) {
+ $self->chat("You have $wget\n");
+ $self->{_backends}{get} = sub {
+ my($self, $uri) = @_;
+ return $self->file_get($uri) if $uri =~ s!^file:/+!/!;
+ $self->safeexec( my $fh, $wget, $uri, ( $self->{verbose} ? () : '-q' ), '-O', '-' ) or die "wget $uri: $!";
+ local $/;
+ <$fh>;
+ };
+ $self->{_backends}{mirror} = sub {
+ my($self, $uri, $path) = @_;
+ return $self->file_mirror($uri, $path) if $uri =~ s!^file:/+!/!;
+ $self->safeexec( my $fh, $wget, '--retry-connrefused', $uri, ( $self->{verbose} ? () : '-q' ), '-O', $path ) or die "wget $uri: $!";
+ local $/;
+ <$fh>;
+ };
+ } elsif ($self->{try_curl} and my $curl = $self->which('curl')) {
+ $self->chat("You have $curl\n");
+ $self->{_backends}{get} = sub {
+ my($self, $uri) = @_;
+ return $self->file_get($uri) if $uri =~ s!^file:/+!/!;
+ $self->safeexec( my $fh, $curl, '-L', ( $self->{verbose} ? () : '-s' ), $uri ) or die "curl $uri: $!";
+ local $/;
+ <$fh>;
+ };
+ $self->{_backends}{mirror} = sub {
+ my($self, $uri, $path) = @_;
+ return $self->file_mirror($uri, $path) if $uri =~ s!^file:/+!/!;
+ $self->safeexec( my $fh, $curl, '-L', $uri, ( $self->{verbose} ? () : '-s' ), '-#', '-o', $path ) or die "curl $uri: $!";
+ local $/;
+ <$fh>;
+ };
+ } else {
+ require HTTP::Tiny;
+ $self->chat("Falling back to HTTP::Tiny $HTTP::Tiny::VERSION\n");
+
+ $self->{_backends}{get} = sub {
+ my $self = shift;
+ my $res = HTTP::Tiny->new->get($_[0]);
+ return unless $res->{success};
+ return $res->{content};
+ };
+ $self->{_backends}{mirror} = sub {
+ my $self = shift;
+ my $res = HTTP::Tiny->new->mirror(@_);
+ return $res->{status};
+ };
+ }
+
+ my $tar = $self->which('tar');
+ my $tar_ver;
+ my $maybe_bad_tar = sub { WIN32 || SUNOS || (($tar_ver = `$tar --version 2>/dev/null`) =~ /GNU.*1\.13/i) };
+
+ if ($tar && !$maybe_bad_tar->()) {
+ chomp $tar_ver;
+ $self->chat("You have $tar: $tar_ver\n");
+ $self->{_backends}{untar} = sub {
+ my($self, $tarfile) = @_;
+
+ my $xf = "xf" . ($self->{verbose} ? 'v' : '');
+ my $ar = $tarfile =~ /bz2$/ ? 'j' : 'z';
+
+ my($root, @others) = `$tar tf$ar $tarfile`
+ or return undef;
+
+ chomp $root;
+ $root =~ s!^\./!!;
+ $root =~ s{^(.+?)/.*$}{$1};
+
+ system "$tar $xf$ar $tarfile";
+ return $root if -d $root;
+
+ $self->diag_fail("Bad archive: $tarfile");
+ return undef;
+ }
+ } elsif ( $tar
+ and my $gzip = $self->which('gzip')
+ and my $bzip2 = $self->which('bzip2')) {
+ $self->chat("You have $tar, $gzip and $bzip2\n");
+ $self->{_backends}{untar} = sub {
+ my($self, $tarfile) = @_;
+
+ my $x = "x" . ($self->{verbose} ? 'v' : '') . "f -";
+ my $ar = $tarfile =~ /bz2$/ ? $bzip2 : $gzip;
+
+ my($root, @others) = `$ar -dc $tarfile | $tar tf -`
+ or return undef;
+
+ chomp $root;
+ $root =~ s{^(.+?)/.*$}{$1};
+
+ system "$ar -dc $tarfile | $tar $x";
+ return $root if -d $root;
+
+ $self->diag_fail("Bad archive: $tarfile");
+ return undef;
+ }
+ } elsif (eval { require Archive::Tar }) { # uses too much memory!
+ $self->chat("Falling back to Archive::Tar $Archive::Tar::VERSION\n");
+ $self->{_backends}{untar} = sub {
+ my $self = shift;
+ my $t = Archive::Tar->new($_[0]);
+ my $root = ($t->list_files)[0];
+ $root =~ s{^(.+?)/.*$}{$1};
+ $t->extract;
+ return -d $root ? $root : undef;
+ };
+ } else {
+ $self->{_backends}{untar} = sub {
+ die "Failed to extract $_[1] - You need to have tar or Archive::Tar installed.\n";
+ };
+ }
+
+ if (my $unzip = $self->which('unzip')) {
+ $self->chat("You have $unzip\n");
+ $self->{_backends}{unzip} = sub {
+ my($self, $zipfile) = @_;
+
+ my $opt = $self->{verbose} ? '' : '-q';
+ my(undef, $root, @others) = `$unzip -t $zipfile`
+ or return undef;
+
+ chomp $root;
+ $root =~ s{^\s+testing:\s+(.+?)/\s+OK$}{$1};
+
+ system "$unzip $opt $zipfile";
+ return $root if -d $root;
+
+ $self->diag_fail("Bad archive: [$root] $zipfile");
+ return undef;
+ }
+ } else {
+ $self->{_backends}{unzip} = sub {
+ eval { require Archive::Zip }
+ or die "Failed to extract $_[1] - You need to have unzip or Archive::Zip installed.\n";
+ my($self, $file) = @_;
+ my $zip = Archive::Zip->new();
+ my $status;
+ $status = $zip->read($file);
+ $self->diag_fail("Read of file[$file] failed")
+ if $status != Archive::Zip::AZ_OK();
+ my @members = $zip->members();
+ my $root;
+ for my $member ( @members ) {
+ my $af = $member->fileName();
+ next if ($af =~ m!^(/|\.\./)!);
+ $root = $af unless $root;
+ $status = $member->extractToFileNamed( $af );
+ $self->diag_fail("Extracting of file[$af] from zipfile[$file failed")
+ if $status != Archive::Zip::AZ_OK();
+ }
+ return -d $root ? $root : undef;
+ };
+ }
+ }
+
+ sub safeexec {
+ my $self = shift;
+ my $rdr = $_[0] ||= Symbol::gensym();
+
+ if (WIN32) {
+ my $cmd = join q{ }, map { $self->shell_quote($_) } @_[ 1 .. $#_ ];
+ return open( $rdr, "$cmd |" );
+ }
+
+ if ( my $pid = open( $rdr, '-|' ) ) {
+ return $pid;
+ }
+ elsif ( defined $pid ) {
+ exec( @_[ 1 .. $#_ ] );
+ exit 1;
+ }
+ else {
+ return;
+ }
+ }
+
+ sub parse_meta {
+ my($self, $file) = @_;
+ return eval { (Parse::CPAN::Meta::LoadFile($file))[0] } || undef;
+ }
+
+ sub parse_meta_string {
+ my($self, $yaml) = @_;
+ return eval { (Parse::CPAN::Meta::Load($yaml))[0] } || undef;
+ }
+
+ 1;
+APP_CPANMINUS_SCRIPT
+
+$fatpacked{"CPAN/DistnameInfo.pm"} = <<'CPAN_DISTNAMEINFO';
+
+ package CPAN::DistnameInfo;
+
+ $VERSION = "0.11";
+ use strict;
+
+ sub distname_info {
+ my $file = shift or return;
+
+ my ($dist, $version) = $file =~ /^
+ ((?:[-+.]*(?:[A-Za-z0-9]+|(?<=\D)_|_(?=\D))*
+ (?:
+ [A-Za-z](?=[^A-Za-z]|$)
+ |
+ \d(?=-)
+ )(?<![._-][vV])
+ )+)(.*)
+ $/xs or return ($file,undef,undef);
+
+ if ($dist =~ /-undef\z/ and ! length $version) {
+ $dist =~ s/-undef\z//;
+ }
+
+ # Remove potential -withoutworldwriteables suffix
+ $version =~ s/-withoutworldwriteables$//;
+
+ if ($version =~ /^(-[Vv].*)-(\d.*)/) {
+
+ # Catch names like Unicode-Collate-Standard-V3_1_1-0.1
+ # where the V3_1_1 is part of the distname
+ $dist .= $1;
+ $version = $2;
+ }
+
+ # Normalize the Dist.pm-1.23 convention which CGI.pm and
+ # a few others use.
+ $dist =~ s{\.pm$}{};
+
+ $version = $1
+ if !length $version and $dist =~ s/-(\d+\w)$//;
+
+ $version = $1 . $version
+ if $version =~ /^\d+$/ and $dist =~ s/-(\w+)$//;
+
+ if ($version =~ /\d\.\d/) {
+ $version =~ s/^[-_.]+//;
+ }
+ else {
+ $version =~ s/^[-_]+//;
+ }
+
+ my $dev;
+ if (length $version) {
+ if ($file =~ /^perl-?\d+\.(\d+)(?:\D(\d+))?(-(?:TRIAL|RC)\d+)?$/) {
+ $dev = 1 if (($1 > 6 and $1 & 1) or ($2 and $2 >= 50)) or $3;
+ }
+ elsif ($version =~ /\d\D\d+_\d/ or $version =~ /-TRIAL/) {
+ $dev = 1;
+ }
+ }
+ else {
+ $version = undef;
+ }
+
+ ($dist, $version, $dev);
+ }
+
+ sub new {
+ my $class = shift;
+ my $distfile = shift;
+
+ $distfile =~ s,//+,/,g;
+
+ my %info = ( pathname => $distfile );
+
+ ($info{filename} = $distfile) =~ s,^(((.*?/)?authors/)?id/)?([A-Z])/(\4[A-Z])/(\5[-A-Z0-9]*)/,,
+ and $info{cpanid} = $6;
+
+ if ($distfile =~ m,([^/]+)\.(tar\.(?:g?z|bz2)|zip|tgz)$,i) { # support more ?
+ $info{distvname} = $1;
+ $info{extension} = $2;
+ }
+
+ @info{qw(dist version beta)} = distname_info($info{distvname});
+ $info{maturity} = delete $info{beta} ? 'developer' : 'released';
+
+ return bless \%info, $class;
+ }
+
+ sub dist { shift->{dist} }
+ sub version { shift->{version} }
+ sub maturity { shift->{maturity} }
+ sub filename { shift->{filename} }
+ sub cpanid { shift->{cpanid} }
+ sub distvname { shift->{distvname} }
+ sub extension { shift->{extension} }
+ sub pathname { shift->{pathname} }
+
+ sub properties { %{ $_[0] } }
+
+ 1;
+
+ __END__
+
+CPAN_DISTNAMEINFO
+
+$fatpacked{"HTTP/Tiny.pm"} = <<'HTTP_TINY';
+ # vim: ts=4 sts=4 sw=4 et:
+ #
+ # This file is part of HTTP-Tiny
+ #
+ # This software is copyright (c) 2011 by Christian Hansen.
+ #
+ # This is free software; you can redistribute it and/or modify it under
+ # the same terms as the Perl 5 programming language system itself.
+ #
+ package HTTP::Tiny;
+ BEGIN {
+ $HTTP::Tiny::VERSION = '0.009';
+ }
+ use strict;
+ use warnings;
+ # ABSTRACT: A small, simple, correct HTTP/1.1 client
+
+ use Carp ();
+
+
+ my @attributes;
+ BEGIN {
+ @attributes = qw(agent default_headers max_redirect max_size proxy timeout);
+ no strict 'refs';
+ for my $accessor ( @attributes ) {
+ *{$accessor} = sub {
+ @_ > 1 ? $_[0]->{$accessor} = $_[1] : $_[0]->{$accessor};
+ };
+ }
+ }
+
+ sub new {
+ my($class, %args) = @_;
+ (my $agent = $class) =~ s{::}{-}g;
+ my $self = {
+ agent => $agent . "/" . ($class->VERSION || 0),
+ max_redirect => 5,
+ timeout => 60,
+ };
+ for my $key ( @attributes ) {
+ $self->{$key} = $args{$key} if exists $args{$key}
+ }
+ return bless $self, $class;
+ }
+
+
+ sub get {
+ my ($self, $url, $args) = @_;
+ @_ == 2 || (@_ == 3 && ref $args eq 'HASH')
+ or Carp::croak(q/Usage: $http->get(URL, [HASHREF])/);
+ return $self->request('GET', $url, $args || {});
+ }
+
+
+ sub mirror {
+ my ($self, $url, $file, $args) = @_;
+ @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
+ or Carp::croak(q/Usage: $http->mirror(URL, FILE, [HASHREF])/);
+ if ( -e $file and my $mtime = (stat($file))[9] ) {
+ $args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime);
+ }
+ my $tempfile = $file . int(rand(2**31));
+ open my $fh, ">", $tempfile
+ or Carp::croak(qq/Error: Could not open temporary file $tempfile for downloading: $!/);
+ $args->{data_callback} = sub { print {$fh} $_[0] };
+ my $response = $self->request('GET', $url, $args);
+ close $fh
+ or Carp::croak(qq/Error: Could not close temporary file $tempfile: $!/);
+ if ( $response->{success} ) {
+ rename $tempfile, $file
+ or Carp::croak "Error replacing $file with $tempfile: $!\n";
+ my $lm = $response->{headers}{'last-modified'};
+ if ( $lm and my $mtime = $self->_parse_http_date($lm) ) {
+ utime $mtime, $mtime, $file;
+ }
+ }
+ $response->{success} ||= $response->{status} eq '304';
+ unlink $tempfile;
+ return $response;
+ }
+
+
+ my %idempotent = map { $_ => 1 } qw/GET HEAD PUT DELETE OPTIONS TRACE/;
+
+ sub request {
+ my ($self, $method, $url, $args) = @_;
+ @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
+ or Carp::croak(q/Usage: $http->request(METHOD, URL, [HASHREF])/);
+ $args ||= {}; # we keep some state in this during _request
+
+ # RFC 2616 Section 8.1.4 mandates a single retry on broken socket
+ my $response;
+ for ( 0 .. 1 ) {
+ $response = eval { $self->_request($method, $url, $args) };
+ last unless $@ && $idempotent{$method}
+ && $@ =~ m{^(?:Socket closed|Unexpected end)};
+ }
+
+ if (my $e = "$@") {
+ $response = {
+ success => q{},
+ status => 599,
+ reason => 'Internal Exception',
+ content => $e,
+ headers => {
+ 'content-type' => 'text/plain',
+ 'content-length' => length $e,
+ }
+ };
+ }
+ return $response;
+ }
+
+ my %DefaultPort = (
+ http => 80,
+ https => 443,
+ );
+
+ sub _request {
+ my ($self, $method, $url, $args) = @_;
+
+ my ($scheme, $host, $port, $path_query) = $self->_split_url($url);
+
+ my $request = {
+ method => $method,
+ scheme => $scheme,
+ host_port => ($port == $DefaultPort{$scheme} ? $host : "$host:$port"),
+ uri => $path_query,
+ headers => {},
+ };
+
+ my $handle = HTTP::Tiny::Handle->new(timeout => $self->{timeout});
+
+ if ($self->{proxy}) {
+ $request->{uri} = "$scheme://$request->{host_port}$path_query";
+ croak(qq/HTTPS via proxy is not supported/)
+ if $request->{scheme} eq 'https';
+ $handle->connect(($self->_split_url($self->{proxy}))[0..2]);
+ }
+ else {
+ $handle->connect($scheme, $host, $port);
+ }
+
+ $self->_prepare_headers_and_cb($request, $args);
+ $handle->write_request($request);
+
+ my $response;
+ do { $response = $handle->read_response_header }
+ until (substr($response->{status},0,1) ne '1');
+
+ if ( my @redir_args = $self->_maybe_redirect($request, $response, $args) ) {
+ $handle->close;
+ return $self->_request(@redir_args, $args);
+ }
+
+ if ($method eq 'HEAD' || $response->{status} =~ /^[23]04/) {
+ # response has no message body
+ }
+ else {
+ my $data_cb = $self->_prepare_data_cb($response, $args);
+ $handle->read_body($data_cb, $response);
+ }
+
+ $handle->close;
+ $response->{success} = substr($response->{status},0,1) eq '2';
+ return $response;
+ }
+
+ sub _prepare_headers_and_cb {
+ my ($self, $request, $args) = @_;
+
+ for ($self->{default_headers}, $args->{headers}) {
+ next unless defined;
+ while (my ($k, $v) = each %$_) {
+ $request->{headers}{lc $k} = $v;
+ }
+ }
+ $request->{headers}{'host'} = $request->{host_port};
+ $request->{headers}{'connection'} = "close";
+ $request->{headers}{'user-agent'} ||= $self->{agent};
+
+ if (defined $args->{content}) {
+ $request->{headers}{'content-type'} ||= "application/octet-stream";
+ if (ref $args->{content} eq 'CODE') {
+ $request->{headers}{'transfer-encoding'} = 'chunked'
+ unless $request->{headers}{'content-length'}
+ || $request->{headers}{'transfer-encoding'};
+ $request->{cb} = $args->{content};
+ }
+ else {
+ my $content = $args->{content};
+ if ( $] ge '5.008' ) {
+ utf8::downgrade($content, 1)
+ or Carp::croak(q/Wide character in request message body/);
+ }
+ $request->{headers}{'content-length'} = length $content
+ unless $request->{headers}{'content-length'}
+ || $request->{headers}{'transfer-encoding'};
+ $request->{cb} = sub { substr $content, 0, length $content, '' };
+ }
+ $request->{trailer_cb} = $args->{trailer_callback}
+ if ref $args->{trailer_callback} eq 'CODE';
+ }
+ return;
+ }
+
+ sub _prepare_data_cb {
+ my ($self, $response, $args) = @_;
+ my $data_cb = $args->{data_callback};
+ $response->{content} = '';
+
+ if (!$data_cb || $response->{status} !~ /^2/) {
+ if (defined $self->{max_size}) {
+ $data_cb = sub {
+ $_[1]->{content} .= $_[0];
+ die(qq/Size of response body exceeds the maximum allowed of $self->{max_size}\n/)
+ if length $_[1]->{content} > $self->{max_size};
+ };
+ }
+ else {
+ $data_cb = sub { $_[1]->{content} .= $_[0] };
+ }
+ }
+ return $data_cb;
+ }
+
+ sub _maybe_redirect {
+ my ($self, $request, $response, $args) = @_;
+ my $headers = $response->{headers};
+ my ($status, $method) = ($response->{status}, $request->{method});
+ if (($status eq '303' or ($status =~ /^30[127]/ && $method =~ /^GET|HEAD$/))
+ and $headers->{location}
+ and ++$args->{redirects} <= $self->{max_redirect}
+ ) {
+ my $location = ($headers->{location} =~ /^\//)
+ ? "$request->{scheme}://$request->{host_port}$headers->{location}"
+ : $headers->{location} ;
+ return (($status eq '303' ? 'GET' : $method), $location);
+ }
+ return;
+ }
+
+ sub _split_url {
+ my $url = pop;
+
+ # URI regex adapted from the URI module
+ my ($scheme, $authority, $path_query) = $url =~ m<\A([^:/?#]+)://([^/?#]*)([^#]*)>
+ or Carp::croak(qq/Cannot parse URL: '$url'/);
+
+ $scheme = lc $scheme;
+ $path_query = "/$path_query" unless $path_query =~ m<\A/>;
+
+ my $host = (length($authority)) ? lc $authority : 'localhost';
+ $host =~ s/\A[^@]*@//; # userinfo
+ my $port = do {
+ $host =~ s/:([0-9]*)\z// && length $1
+ ? $1
+ : ($scheme eq 'http' ? 80 : $scheme eq 'https' ? 443 : undef);
+ };
+
+ return ($scheme, $host, $port, $path_query);
+ }
+
+ # Date conversions adapted from HTTP::Date
+ my $DoW = "Sun|Mon|Tue|Wed|Thu|Fri|Sat";
+ my $MoY = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec";
+ sub _http_date {
+ my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($_[1]);
+ return sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT",
+ substr($DoW,$wday*4,3),
+ $mday, substr($MoY,$mon*4,3), $year+1900,
+ $hour, $min, $sec
+ );
+ }
+
+ sub _parse_http_date {
+ my ($self, $str) = @_;
+ require Time::Local;
+ my @tl_parts;
+ if ($str =~ /^[SMTWF][a-z]+, +(\d{1,2}) ($MoY) +(\d\d\d\d) +(\d\d):(\d\d):(\d\d) +GMT$/) {
+ @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
+ }
+ elsif ($str =~ /^[SMTWF][a-z]+, +(\d\d)-($MoY)-(\d{2,4}) +(\d\d):(\d\d):(\d\d) +GMT$/ ) {
+ @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
+ }
+ elsif ($str =~ /^[SMTWF][a-z]+ +($MoY) +(\d{1,2}) +(\d\d):(\d\d):(\d\d) +(?:[^0-9]+ +)?(\d\d\d\d)$/ ) {
+ @tl_parts = ($5, $4, $3, $2, (index($MoY,$1)/4), $6);
+ }
+ return eval {
+ my $t = @tl_parts ? Time::Local::timegm(@tl_parts) : -1;
+ $t < 0 ? undef : $t;
+ };
+ }
+
+ package
+ HTTP::Tiny::Handle; # hide from PAUSE/indexers
+ use strict;
+ use warnings;
+
+ use Carp qw[croak];
+ use Errno qw[EINTR EPIPE];
+ use IO::Socket qw[SOCK_STREAM];
+
+ sub BUFSIZE () { 32768 }
+
+ my $Printable = sub {
+ local $_ = shift;
+ s/\r/\\r/g;
+ s/\n/\\n/g;
+ s/\t/\\t/g;
+ s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
+ $_;
+ };
+
+ my $Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
+
+ sub new {
+ my ($class, %args) = @_;
+ return bless {
+ rbuf => '',
+ timeout => 60,
+ max_line_size => 16384,
+ max_header_lines => 64,
+ %args
+ }, $class;
+ }
+
+ my $ssl_verify_args = {
+ check_cn => "when_only",
+ wildcards_in_alt => "anywhere",
+ wildcards_in_cn => "anywhere"
+ };
+
+ sub connect {
+ @_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
+ my ($self, $scheme, $host, $port) = @_;
+
+ if ( $scheme eq 'https' ) {
+ eval "require IO::Socket::SSL"
+ unless exists $INC{'IO/Socket/SSL.pm'};
+ croak(qq/IO::Socket::SSL must be installed for https support\n/)
+ unless $INC{'IO/Socket/SSL.pm'};
+ }
+ elsif ( $scheme ne 'http' ) {
+ croak(qq/Unsupported URL scheme '$scheme'/);
+ }
+
+ $self->{fh} = 'IO::Socket::INET'->new(
+ PeerHost => $host,
+ PeerPort => $port,
+ Proto => 'tcp',
+ Type => SOCK_STREAM,
+ Timeout => $self->{timeout}
+ ) or croak(qq/Could not connect to '$host:$port': $@/);
+
+ binmode($self->{fh})
+ or croak(qq/Could not binmode() socket: '$!'/);
+
+ if ( $scheme eq 'https') {
+ IO::Socket::SSL->start_SSL($self->{fh});
+ ref($self->{fh}) eq 'IO::Socket::SSL'
+ or die(qq/SSL connection failed for $host\n/);
+ $self->{fh}->verify_hostname( $host, $ssl_verify_args )
+ or die(qq/SSL certificate not valid for $host\n/);
+ }
+
+ $self->{host} = $host;
+ $self->{port} = $port;
+
+ return $self;
+ }
+
+ sub close {
+ @_ == 1 || croak(q/Usage: $handle->close()/);
+ my ($self) = @_;
+ CORE::close($self->{fh})
+ or croak(qq/Could not close socket: '$!'/);
+ }
+
+ sub write {
+ @_ == 2 || croak(q/Usage: $handle->write(buf)/);
+ my ($self, $buf) = @_;
+
+ if ( $] ge '5.008' ) {
+ utf8::downgrade($buf, 1)
+ or croak(q/Wide character in write()/);
+ }
+
+ my $len = length $buf;
+ my $off = 0;
+
+ local $SIG{PIPE} = 'IGNORE';
+
+ while () {
+ $self->can_write
+ or croak(q/Timed out while waiting for socket to become ready for writing/);
+ my $r = syswrite($self->{fh}, $buf, $len, $off);
+ if (defined $r) {
+ $len -= $r;
+ $off += $r;
+ last unless $len > 0;
+ }
+ elsif ($! == EPIPE) {
+ croak(qq/Socket closed by remote server: $!/);
+ }
+ elsif ($! != EINTR) {
+ croak(qq/Could not write to socket: '$!'/);
+ }
+ }
+ return $off;
+ }
+
+ sub read {
+ @_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len [, allow_partial])/);
+ my ($self, $len, $allow_partial) = @_;
+
+ my $buf = '';
+ my $got = length $self->{rbuf};
+
+ if ($got) {
+ my $take = ($got < $len) ? $got : $len;
+ $buf = substr($self->{rbuf}, 0, $take, '');
+ $len -= $take;
+ }
+
+ while ($len > 0) {
+ $self->can_read
+ or croak(q/Timed out while waiting for socket to become ready for reading/);
+ my $r = sysread($self->{fh}, $buf, $len, length $buf);
+ if (defined $r) {
+ last unless $r;
+ $len -= $r;
+ }
+ elsif ($! != EINTR) {
+ croak(qq/Could not read from socket: '$!'/);
+ }
+ }
+ if ($len && !$allow_partial) {
+ croak(q/Unexpected end of stream/);
+ }
+ return $buf;
+ }
+
+ sub readline {
+ @_ == 1 || croak(q/Usage: $handle->readline()/);
+ my ($self) = @_;
+
+ while () {
+ if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
+ return $1;
+ }
+ if (length $self->{rbuf} >= $self->{max_line_size}) {
+ croak(qq/Line size exceeds the maximum allowed size of $self->{max_line_size}/);
+ }
+ $self->can_read
+ or croak(q/Timed out while waiting for socket to become ready for reading/);
+ my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
+ if (defined $r) {
+ last unless $r;
+ }
+ elsif ($! != EINTR) {
+ croak(qq/Could not read from socket: '$!'/);
+ }
+ }
+ croak(q/Unexpected end of stream while looking for line/);
+ }
+
+ sub read_header_lines {
+ @_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
+ my ($self, $headers) = @_;
+ $headers ||= {};
+ my $lines = 0;
+ my $val;
+
+ while () {
+ my $line = $self->readline;
+
+ if (++$lines >= $self->{max_header_lines}) {
+ croak(qq/Header lines exceeds maximum number allowed of $self->{max_header_lines}/);
+ }
+ elsif ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
+ my ($field_name) = lc $1;
+ if (exists $headers->{$field_name}) {
+ for ($headers->{$field_name}) {
+ $_ = [$_] unless ref $_ eq "ARRAY";
+ push @$_, $2;
+ $val = \$_->[-1];
+ }
+ }
+ else {
+ $val = \($headers->{$field_name} = $2);
+ }
+ }
+ elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
+ $val
+ or croak(q/Unexpected header continuation line/);
+ next unless length $1;
+ $$val .= ' ' if length $$val;
+ $$val .= $1;
+ }
+ elsif ($line =~ /\A \x0D?\x0A \z/x) {
+ last;
+ }
+ else {
+ croak(q/Malformed header line: / . $Printable->($line));
+ }
+ }
+ return $headers;
+ }
+
+ sub write_request {
+ @_ == 2 || croak(q/Usage: $handle->write_request(request)/);
+ my($self, $request) = @_;
+ $self->write_request_header(@{$request}{qw/method uri headers/});
+ $self->write_body($request) if $request->{cb};
+ return;
+ }
+
+ my %HeaderCase = (
+ 'content-md5' => 'Content-MD5',
+ 'etag' => 'ETag',
+ 'te' => 'TE',
+ 'www-authenticate' => 'WWW-Authenticate',
+ 'x-xss-protection' => 'X-XSS-Protection',
+ );
+
+ sub write_header_lines {
+ (@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
+ my($self, $headers) = @_;
+
+ my $buf = '';
+ while (my ($k, $v) = each %$headers) {
+ my $field_name = lc $k;
+ if (exists $HeaderCase{$field_name}) {
+ $field_name = $HeaderCase{$field_name};
+ }
+ else {
+ $field_name =~ /\A $Token+ \z/xo
+ or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
+ $field_name =~ s/\b(\w)/\u$1/g;
+ $HeaderCase{lc $field_name} = $field_name;
+ }
+ for (ref $v eq 'ARRAY' ? @$v : $v) {
+ /[^\x0D\x0A]/
+ or croak(qq/Invalid HTTP header field value ($field_name): / . $Printable->($_));
+ $buf .= "$field_name: $_\x0D\x0A";
+ }
+ }
+ $buf .= "\x0D\x0A";
+ return $self->write($buf);
+ }
+
+ sub read_body {
+ @_ == 3 || croak(q/Usage: $handle->read_body(callback, response)/);
+ my ($self, $cb, $response) = @_;
+ my $te = $response->{headers}{'transfer-encoding'} || '';
+ if ( grep { /chunked/i } ( ref $te eq 'ARRAY' ? @$te : $te ) ) {
+ $self->read_chunked_body($cb, $response);
+ }
+ else {
+ $self->read_content_body($cb, $response);
+ }
+ return;
+ }
+
+ sub write_body {
+ @_ == 2 || croak(q/Usage: $handle->write_body(request)/);
+ my ($self, $request) = @_;
+ if ($request->{headers}{'content-length'}) {
+ return $self->write_content_body($request);
+ }
+ else {
+ return $self->write_chunked_body($request);
+ }
+ }
+
+ sub read_content_body {
+ @_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
+ my ($self, $cb, $response, $content_length) = @_;
+ $content_length ||= $response->{headers}{'content-length'};
+
+ if ( $content_length ) {
+ my $len = $content_length;
+ while ($len > 0) {
+ my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
+ $cb->($self->read($read, 0), $response);
+ $len -= $read;
+ }
+ }
+ else {
+ my $chunk;
+ $cb->($chunk, $response) while length( $chunk = $self->read(BUFSIZE, 1) );
+ }
+
+ return;
+ }
+
+ sub write_content_body {
+ @_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
+ my ($self, $request) = @_;
+
+ my ($len, $content_length) = (0, $request->{headers}{'content-length'});
+ while () {
+ my $data = $request->{cb}->();
+
+ defined $data && length $data
+ or last;
+
+ if ( $] ge '5.008' ) {
+ utf8::downgrade($data, 1)
+ or croak(q/Wide character in write_content()/);
+ }
+
+ $len += $self->write($data);
+ }
+
+ $len == $content_length
+ or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
+
+ return $len;
+ }
+
+ sub read_chunked_body {
+ @_ == 3 || croak(q/Usage: $handle->read_chunked_body(callback, $response)/);
+ my ($self, $cb, $response) = @_;
+
+ while () {
+ my $head = $self->readline;
+
+ $head =~ /\A ([A-Fa-f0-9]+)/x
+ or croak(q/Malformed chunk head: / . $Printable->($head));
+
+ my $len = hex($1)
+ or last;
+
+ $self->read_content_body($cb, $response, $len);
+
+ $self->read(2) eq "\x0D\x0A"
+ or croak(q/Malformed chunk: missing CRLF after chunk data/);
+ }
+ $self->read_header_lines($response->{headers});
+ return;
+ }
+
+ sub write_chunked_body {
+ @_ == 2 || croak(q/Usage: $handle->write_chunked_body(request)/);
+ my ($self, $request) = @_;
+
+ my $len = 0;
+ while () {
+ my $data = $request->{cb}->();
+
+ defined $data && length $data
+ or last;
+
+ if ( $] ge '5.008' ) {
+ utf8::downgrade($data, 1)
+ or croak(q/Wide character in write_chunked_body()/);
+ }
+
+ $len += length $data;
+
+ my $chunk = sprintf '%X', length $data;
+ $chunk .= "\x0D\x0A";
+ $chunk .= $data;
+ $chunk .= "\x0D\x0A";
+
+ $self->write($chunk);
+ }
+ $self->write("0\x0D\x0A");
+ $self->write_header_lines($request->{trailer_cb}->())
+ if ref $request->{trailer_cb} eq 'CODE';
+ return $len;
+ }
+
+ sub read_response_header {
+ @_ == 1 || croak(q/Usage: $handle->read_response_header()/);
+ my ($self) = @_;
+
+ my $line = $self->readline;
+
+ $line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
+ or croak(q/Malformed Status-Line: / . $Printable->($line));
+
+ my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
+
+ croak (qq/Unsupported HTTP protocol: $protocol/)
+ unless $version =~ /0*1\.0*[01]/;
+
+ return {
+ status => $status,
+ reason => $reason,
+ headers => $self->read_header_lines,
+ protocol => $protocol,
+ };
+ }
+
+ sub write_request_header {
+ @_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
+ my ($self, $method, $request_uri, $headers) = @_;
+
+ return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
+ + $self->write_header_lines($headers);
+ }
+
+ sub _do_timeout {
+ my ($self, $type, $timeout) = @_;
+ $timeout = $self->{timeout}
+ unless defined $timeout && $timeout >= 0;
+
+ my $fd = fileno $self->{fh};
+ defined $fd && $fd >= 0
+ or croak(q/select(2): 'Bad file descriptor'/);
+
+ my $initial = time;
+ my $pending = $timeout;
+ my $nfound;
+
+ vec(my $fdset = '', $fd, 1) = 1;
+
+ while () {
+ $nfound = ($type eq 'read')
+ ? select($fdset, undef, undef, $pending)
+ : select(undef, $fdset, undef, $pending) ;
+ if ($nfound == -1) {
+ $! == EINTR
+ or croak(qq/select(2): '$!'/);
+ redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
+ $nfound = 0;
+ }
+ last;
+ }
+ $! = 0;
+ return $nfound;
+ }
+
+ sub can_read {
+ @_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
+ my $self = shift;
+ return $self->_do_timeout('read', @_)
+ }
+
+ sub can_write {
+ @_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
+ my $self = shift;
+ return $self->_do_timeout('write', @_)
+ }
+
+ 1;
+
+
+
+ __END__
+ =pod
+
+HTTP_TINY
+
+$fatpacked{"Module/Metadata.pm"} = <<'MODULE_METADATA';
+ # -*- mode: cperl; tab-width: 8; indent-tabs-mode: nil; basic-offset: 2 -*-
+ # vim:ts=8:sw=2:et:sta:sts=2
+ package Module::Metadata;
+
+ # Adapted from Perl-licensed code originally distributed with
+ # Module-Build by Ken Williams
+
+ # This module provides routines to gather information about
+ # perl modules (assuming this may be expanded in the distant
+ # parrot future to look at other types of modules).
+
+ use strict;
+ use vars qw($VERSION);
+ $VERSION = '1.000003';
+ $VERSION = eval $VERSION;
+
+ use File::Spec;
+ use IO::File;
+ use version 0.87;
+ BEGIN {
+ if ($INC{'Log/Contextual.pm'}) {
+ Log::Contextual->import('log_info');
+ } else {
+ *log_info = sub (&) { warn $_[0]->() };
+ }
+ }
+ use File::Find qw(find);
+
+ my $V_NUM_REGEXP = qr{v?[0-9._]+}; # crudely, a v-string or decimal
+
+ my $PKG_REGEXP = qr{ # match a package declaration
+ ^[\s\{;]* # intro chars on a line
+ package # the word 'package'
+ \s+ # whitespace
+ ([\w:]+) # a package name
+ \s* # optional whitespace
+ ($V_NUM_REGEXP)? # optional version number
+ \s* # optional whitesapce
+ ; # semicolon line terminator
+ }x;
+
+ my $VARNAME_REGEXP = qr{ # match fully-qualified VERSION name
+ ([\$*]) # sigil - $ or *
+ (
+ ( # optional leading package name
+ (?:::|\')? # possibly starting like just :: (Ì la $::VERSION)
+ (?:\w+(?:::|\'))* # Foo::Bar:: ...
+ )?
+ VERSION
+ )\b
+ }x;
+
+ my $VERS_REGEXP = qr{ # match a VERSION definition
+ (?:
+ \(\s*$VARNAME_REGEXP\s*\) # with parens
+ |
+ $VARNAME_REGEXP # without parens
+ )
+ \s*
+ =[^=~] # = but not ==, nor =~
+ }x;
+
+
+ sub new_from_file {
+ my $class = shift;
+ my $filename = File::Spec->rel2abs( shift );
+
+ return undef unless defined( $filename ) && -f $filename;
+ return $class->_init(undef, $filename, @_);
+ }
+
+ sub new_from_module {
+ my $class = shift;
+ my $module = shift;
+ my %props = @_;
+
+ $props{inc} ||= \@INC;
+ my $filename = $class->find_module_by_name( $module, $props{inc} );
+ return undef unless defined( $filename ) && -f $filename;
+ return $class->_init($module, $filename, %props);
+ }
+
+ {
+
+ my $compare_versions = sub {
+ my ($v1, $op, $v2) = @_;
+ $v1 = version->new($v1)
+ unless UNIVERSAL::isa($v1,'version');
+
+ my $eval_str = "\$v1 $op \$v2";
+ my $result = eval $eval_str;
+ log_info { "error comparing versions: '$eval_str' $@" } if $@;
+
+ return $result;
+ };
+
+ my $normalize_version = sub {
+ my ($version) = @_;
+ if ( $version =~ /[=<>!,]/ ) { # logic, not just version
+ # take as is without modification
+ }
+ elsif ( ref $version eq 'version' ) { # version objects
+ $version = $version->is_qv ? $version->normal : $version->stringify;
+ }
+ elsif ( $version =~ /^[^v][^.]*\.[^.]+\./ ) { # no leading v, multiple dots
+ # normalize string tuples without "v": "1.2.3" -> "v1.2.3"
+ $version = "v$version";
+ }
+ else {
+ # leave alone
+ }
+ return $version;
+ };
+
+ # separate out some of the conflict resolution logic
+
+ my $resolve_module_versions = sub {
+ my $packages = shift;
+
+ my( $file, $version );
+ my $err = '';
+ foreach my $p ( @$packages ) {
+ if ( defined( $p->{version} ) ) {
+ if ( defined( $version ) ) {
+ if ( $compare_versions->( $version, '!=', $p->{version} ) ) {
+ $err .= " $p->{file} ($p->{version})\n";
+ } else {
+ # same version declared multiple times, ignore
+ }
+ } else {
+ $file = $p->{file};
+ $version = $p->{version};
+ }
+ }
+ $file ||= $p->{file} if defined( $p->{file} );
+ }
+
+ if ( $err ) {
+ $err = " $file ($version)\n" . $err;
+ }
+
+ my %result = (
+ file => $file,
+ version => $version,
+ err => $err
+ );
+
+ return \%result;
+ };
+
+ sub package_versions_from_directory {
+ my ( $class, $dir, $files ) = @_;
+
+ my @files;
+
+ if ( $files ) {
+ @files = @$files;
+ } else {
+ find( {
+ wanted => sub {
+ push @files, $_ if -f $_ && /\.pm$/;
+ },
+ no_chdir => 1,
+ }, $dir );
+ }
+
+ # First, we enumerate all packages & versions,
+ # separating into primary & alternative candidates
+ my( %prime, %alt );
+ foreach my $file (@files) {
+ my $mapped_filename = File::Spec->abs2rel( $file, $dir );
+ my @path = split( /\//, $mapped_filename );
+ (my $prime_package = join( '::', @path )) =~ s/\.pm$//;
+
+ my $pm_info = $class->new_from_file( $file );
+
+ foreach my $package ( $pm_info->packages_inside ) {
+ next if $package eq 'main'; # main can appear numerous times, ignore
+ next if $package eq 'DB'; # special debugging package, ignore
+ next if grep /^_/, split( /::/, $package ); # private package, ignore
+
+ my $version = $pm_info->version( $package );
+
+ if ( $package eq $prime_package ) {
+ if ( exists( $prime{$package} ) ) {
+ # M::B::ModuleInfo will handle this conflict
+ die "Unexpected conflict in '$package'; multiple versions found.\n";
+ } else {
+ $prime{$package}{file} = $mapped_filename;
+ $prime{$package}{version} = $version if defined( $version );
+ }
+ } else {
+ push( @{$alt{$package}}, {
+ file => $mapped_filename,
+ version => $version,
+ } );
+ }
+ }
+ }
+
+ # Then we iterate over all the packages found above, identifying conflicts
+ # and selecting the "best" candidate for recording the file & version
+ # for each package.
+ foreach my $package ( keys( %alt ) ) {
+ my $result = $resolve_module_versions->( $alt{$package} );
+
+ if ( exists( $prime{$package} ) ) { # primary package selected
+
+ if ( $result->{err} ) {
+ # Use the selected primary package, but there are conflicting
+ # errors among multiple alternative packages that need to be
+ # reported
+ log_info {
+ "Found conflicting versions for package '$package'\n" .
+ " $prime{$package}{file} ($prime{$package}{version})\n" .
+ $result->{err}
+ };
+
+ } elsif ( defined( $result->{version} ) ) {
+ # There is a primary package selected, and exactly one
+ # alternative package
+
+ if ( exists( $prime{$package}{version} ) &&
+ defined( $prime{$package}{version} ) ) {
+ # Unless the version of the primary package agrees with the
+ # version of the alternative package, report a conflict
+ if ( $compare_versions->(
+ $prime{$package}{version}, '!=', $result->{version}
+ )
+ ) {
+
+ log_info {
+ "Found conflicting versions for package '$package'\n" .
+ " $prime{$package}{file} ($prime{$package}{version})\n" .
+ " $result->{file} ($result->{version})\n"
+ };
+ }
+
+ } else {
+ # The prime package selected has no version so, we choose to
+ # use any alternative package that does have a version
+ $prime{$package}{file} = $result->{file};
+ $prime{$package}{version} = $result->{version};
+ }
+
+ } else {
+ # no alt package found with a version, but we have a prime
+ # package so we use it whether it has a version or not
+ }
+
+ } else { # No primary package was selected, use the best alternative
+
+ if ( $result->{err} ) {
+ log_info {
+ "Found conflicting versions for package '$package'\n" .
+ $result->{err}
+ };
+ }
+
+ # Despite possible conflicting versions, we choose to record
+ # something rather than nothing
+ $prime{$package}{file} = $result->{file};
+ $prime{$package}{version} = $result->{version}
+ if defined( $result->{version} );
+ }
+ }
+
+ # Normalize versions. Can't use exists() here because of bug in YAML::Node.
+ # XXX "bug in YAML::Node" comment seems irrelvant -- dagolden, 2009-05-18
+ for (grep defined $_->{version}, values %prime) {
+ $_->{version} = $normalize_version->( $_->{version} );
+ }
+
+ return \%prime;
+ }
+ }
+
+
+ sub _init {
+ my $class = shift;
+ my $module = shift;
+ my $filename = shift;
+ my %props = @_;
+
+ my( %valid_props, @valid_props );
+ @valid_props = qw( collect_pod inc );
+ @valid_props{@valid_props} = delete( @props{@valid_props} );
+ warn "Unknown properties: @{[keys %props]}\n" if scalar( %props );
+
+ my %data = (
+ module => $module,
+ filename => $filename,
+ version => undef,
+ packages => [],
+ versions => {},
+ pod => {},
+ pod_headings => [],
+ collect_pod => 0,
+
+ %valid_props,
+ );
+
+ my $self = bless(\%data, $class);
+
+ $self->_parse_file();
+
+ unless($self->{module} and length($self->{module})) {
+ my ($v, $d, $f) = File::Spec->splitpath($self->{filename});
+ if($f =~ /\.pm$/) {
+ $f =~ s/\..+$//;
+ my @candidates = grep /$f$/, @{$self->{packages}};
+ $self->{module} = shift(@candidates); # punt
+ }
+ else {
+ if(grep /main/, @{$self->{packages}}) {
+ $self->{module} = 'main';
+ }
+ else {
+ $self->{module} = $self->{packages}[0] || '';
+ }
+ }
+ }
+
+ $self->{version} = $self->{versions}{$self->{module}}
+ if defined( $self->{module} );
+
+ return $self;
+ }
+
+ # class method
+ sub _do_find_module {
+ my $class = shift;
+ my $module = shift || die 'find_module_by_name() requires a package name';
+ my $dirs = shift || \@INC;
+
+ my $file = File::Spec->catfile(split( /::/, $module));
+ foreach my $dir ( @$dirs ) {
+ my $testfile = File::Spec->catfile($dir, $file);
+ return [ File::Spec->rel2abs( $testfile ), $dir ]
+ if -e $testfile and !-d _; # For stuff like ExtUtils::xsubpp
+ return [ File::Spec->rel2abs( "$testfile.pm" ), $dir ]
+ if -e "$testfile.pm";
+ }
+ return;
+ }
+
+ # class method
+ sub find_module_by_name {
+ my $found = shift()->_do_find_module(@_) or return;
+ return $found->[0];
+ }
+
+ # class method
+ sub find_module_dir_by_name {
+ my $found = shift()->_do_find_module(@_) or return;
+ return $found->[1];
+ }
+
+
+ # given a line of perl code, attempt to parse it if it looks like a
+ # $VERSION assignment, returning sigil, full name, & package name
+ sub _parse_version_expression {
+ my $self = shift;
+ my $line = shift;
+
+ my( $sig, $var, $pkg );
+ if ( $line =~ $VERS_REGEXP ) {
+ ( $sig, $var, $pkg ) = $2 ? ( $1, $2, $3 ) : ( $4, $5, $6 );
+ if ( $pkg ) {
+ $pkg = ($pkg eq '::') ? 'main' : $pkg;
+ $pkg =~ s/::$//;
+ }
+ }
+
+ return ( $sig, $var, $pkg );
+ }
+
+ sub _parse_file {
+ my $self = shift;
+
+ my $filename = $self->{filename};
+ my $fh = IO::File->new( $filename )
+ or die( "Can't open '$filename': $!" );
+
+ $self->_parse_fh($fh);
+ }
+
+ sub _parse_fh {
+ my ($self, $fh) = @_;
+
+ my( $in_pod, $seen_end, $need_vers ) = ( 0, 0, 0 );
+ my( @pkgs, %vers, %pod, @pod );
+ my $pkg = 'main';
+ my $pod_sect = '';
+ my $pod_data = '';
+
+ while (defined( my $line = <$fh> )) {
+ my $line_num = $.;
+
+ chomp( $line );
+ next if $line =~ /^\s*#/;
+
+ $in_pod = ($line =~ /^=(?!cut)/) ? 1 : ($line =~ /^=cut/) ? 0 : $in_pod;
+
+ # Would be nice if we could also check $in_string or something too
+ last if !$in_pod && $line =~ /^__(?:DATA|END)__$/;
+
+ if ( $in_pod || $line =~ /^=cut/ ) {
+
+ if ( $line =~ /^=head\d\s+(.+)\s*$/ ) {
+ push( @pod, $1 );
+ if ( $self->{collect_pod} && length( $pod_data ) ) {
+ $pod{$pod_sect} = $pod_data;
+ $pod_data = '';
+ }
+ $pod_sect = $1;
+
+
+ } elsif ( $self->{collect_pod} ) {
+ $pod_data .= "$line\n";
+
+ }
+
+ } else {
+
+ $pod_sect = '';
+ $pod_data = '';
+
+ # parse $line to see if it's a $VERSION declaration
+ my( $vers_sig, $vers_fullname, $vers_pkg ) =
+ $self->_parse_version_expression( $line );
+
+ if ( $line =~ $PKG_REGEXP ) {
+ $pkg = $1;
+ push( @pkgs, $pkg ) unless grep( $pkg eq $_, @pkgs );
+ $vers{$pkg} = (defined $2 ? $2 : undef) unless exists( $vers{$pkg} );
+ $need_vers = defined $2 ? 0 : 1;
+
+ # VERSION defined with full package spec, i.e. $Module::VERSION
+ } elsif ( $vers_fullname && $vers_pkg ) {
+ push( @pkgs, $vers_pkg ) unless grep( $vers_pkg eq $_, @pkgs );
+ $need_vers = 0 if $vers_pkg eq $pkg;
+
+ unless ( defined $vers{$vers_pkg} && length $vers{$vers_pkg} ) {
+ $vers{$vers_pkg} =
+ $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line );
+ } else {
+ # Warn unless the user is using the "$VERSION = eval
+ # $VERSION" idiom (though there are probably other idioms
+ # that we should watch out for...)
+ warn <<"EOM" unless $line =~ /=\s*eval/;
+ Package '$vers_pkg' already declared with version '$vers{$vers_pkg}',
+ ignoring subsequent declaration on line $line_num.
+ EOM
+ }
+
+ # first non-comment line in undeclared package main is VERSION
+ } elsif ( !exists($vers{main}) && $pkg eq 'main' && $vers_fullname ) {
+ $need_vers = 0;
+ my $v =
+ $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line );
+ $vers{$pkg} = $v;
+ push( @pkgs, 'main' );
+
+ # first non-comment line in undeclared package defines package main
+ } elsif ( !exists($vers{main}) && $pkg eq 'main' && $line =~ /\w+/ ) {
+ $need_vers = 1;
+ $vers{main} = '';
+ push( @pkgs, 'main' );
+
+ # only keep if this is the first $VERSION seen
+ } elsif ( $vers_fullname && $need_vers ) {
+ $need_vers = 0;
+ my $v =
+ $self->_evaluate_version_line( $vers_sig, $vers_fullname, $line );
+
+
+ unless ( defined $vers{$pkg} && length $vers{$pkg} ) {
+ $vers{$pkg} = $v;
+ } else {
+ warn <<"EOM";
+ Package '$pkg' already declared with version '$vers{$pkg}'
+ ignoring new version '$v' on line $line_num.
+ EOM
+ }
+
+ }
+
+ }
+
+ }
+
+ if ( $self->{collect_pod} && length($pod_data) ) {
+ $pod{$pod_sect} = $pod_data;
+ }
+
+ $self->{versions} = \%vers;
+ $self->{packages} = \@pkgs;
+ $self->{pod} = \%pod;
+ $self->{pod_headings} = \@pod;
+ }
+
+ {
+ my $pn = 0;
+ sub _evaluate_version_line {
+ my $self = shift;
+ my( $sigil, $var, $line ) = @_;
+
+ # Some of this code came from the ExtUtils:: hierarchy.
+
+ # We compile into $vsub because 'use version' would cause
+ # compiletime/runtime issues with local()
+ my $vsub;
+ $pn++; # everybody gets their own package
+ my $eval = qq{BEGIN { q# Hide from _packages_inside()
+ #; package Module::Metadata::_version::p$pn;
+ use version;
+ no strict;
+
+ local $sigil$var;
+ \$$var=undef;
+ \$vsub = sub {
+ $line;
+ \$$var
+ };
+ }};
+
+ local $^W;
+ # Try to get the $VERSION
+ eval $eval;
+ # some modules say $VERSION = $Foo::Bar::VERSION, but Foo::Bar isn't
+ # installed, so we need to hunt in ./lib for it
+ if ( $@ =~ /Can't locate/ && -d 'lib' ) {
+ local @INC = ('lib',@INC);
+ eval $eval;
+ }
+ warn "Error evaling version line '$eval' in $self->{filename}: $@\n"
+ if $@;
+ (ref($vsub) eq 'CODE') or
+ die "failed to build version sub for $self->{filename}";
+ my $result = eval { $vsub->() };
+ die "Could not get version from $self->{filename} by executing:\n$eval\n\nThe fatal error was: $@\n"
+ if $@;
+
+ # Upgrade it into a version object
+ my $version = eval { _dwim_version($result) };
+
+ die "Version '$result' from $self->{filename} does not appear to be valid:\n$eval\n\nThe fatal error was: $@\n"
+ unless defined $version; # "0" is OK!
+
+ return $version;
+ }
+ }
+
+ # Try to DWIM when things fail the lax version test in obvious ways
+ {
+ my @version_prep = (
+ # Best case, it just works
+ sub { return shift },
+
+ # If we still don't have a version, try stripping any
+ # trailing junk that is prohibited by lax rules
+ sub {
+ my $v = shift;
+ $v =~ s{([0-9])[a-z-].*$}{$1}i; # 1.23-alpha or 1.23b
+ return $v;
+ },
+
+ # Activestate apparently creates custom versions like '1.23_45_01', which
+ # cause version.pm to think it's an invalid alpha. So check for that
+ # and strip them
+ sub {
+ my $v = shift;
+ my $num_dots = () = $v =~ m{(\.)}g;
+ my $num_unders = () = $v =~ m{(_)}g;
+ my $leading_v = substr($v,0,1) eq 'v';
+ if ( ! $leading_v && $num_dots < 2 && $num_unders > 1 ) {
+ $v =~ s{_}{}g;
+ $num_unders = () = $v =~ m{(_)}g;
+ }
+ return $v;
+ },
+
+ # Worst case, try numifying it like we would have before version objects
+ sub {
+ my $v = shift;
+ no warnings 'numeric';
+ return 0 + $v;
+ },
+
+ );
+
+ sub _dwim_version {
+ my ($result) = shift;
+
+ return $result if ref($result) eq 'version';
+
+ my ($version, $error);
+ for my $f (@version_prep) {
+ $result = $f->($result);
+ $version = eval { version->new($result) };
+ $error ||= $@ if $@; # capture first failure
+ last if defined $version;
+ }
+
+ die $error unless defined $version;
+
+ return $version;
+ }
+ }
+
+ ############################################################
+
+ # accessors
+ sub name { $_[0]->{module} }
+
+ sub filename { $_[0]->{filename} }
+ sub packages_inside { @{$_[0]->{packages}} }
+ sub pod_inside { @{$_[0]->{pod_headings}} }
+ sub contains_pod { $#{$_[0]->{pod_headings}} }
+
+ sub version {
+ my $self = shift;
+ my $mod = shift || $self->{module};
+ my $vers;
+ if ( defined( $mod ) && length( $mod ) &&
+ exists( $self->{versions}{$mod} ) ) {
+ return $self->{versions}{$mod};
+ } else {
+ return undef;
+ }
+ }
+
+ sub pod {
+ my $self = shift;
+ my $sect = shift;
+ if ( defined( $sect ) && length( $sect ) &&
+ exists( $self->{pod}{$sect} ) ) {
+ return $self->{pod}{$sect};
+ } else {
+ return undef;
+ }
+ }
+
+ 1;
+
+MODULE_METADATA
+
+$fatpacked{"Parse/CPAN/Meta.pm"} = <<'PARSE_CPAN_META';
+ package Parse::CPAN::Meta;
+
+ use strict;
+ use Carp 'croak';
+
+ # UTF Support?
+ sub HAVE_UTF8 () { $] >= 5.007003 }
+ BEGIN {
+ if ( HAVE_UTF8 ) {
+ # The string eval helps hide this from Test::MinimumVersion
+ eval "require utf8;";
+ die "Failed to load UTF-8 support" if $@;
+ }
+
+ # Class structure
+ require 5.004;
+ require Exporter;
+ $Parse::CPAN::Meta::VERSION = '1.40';
+ @Parse::CPAN::Meta::ISA = qw{ Exporter };
+ @Parse::CPAN::Meta::EXPORT_OK = qw{ Load LoadFile };
+ }
+
+ # Prototypes
+ sub LoadFile ($);
+ sub Load ($);
+ sub _scalar ($$$);
+ sub _array ($$$);
+ sub _hash ($$$);
+
+ # Printable characters for escapes
+ my %UNESCAPES = (
+ z => "\x00", a => "\x07", t => "\x09",
+ n => "\x0a", v => "\x0b", f => "\x0c",
+ r => "\x0d", e => "\x1b", '\\' => '\\',
+ );
+
+
+
+
+
+ #####################################################################
+ # Implementation
+
+ # Create an object from a file
+ sub LoadFile ($) {
+ # Check the file
+ my $file = shift;
+ croak('You did not specify a file name') unless $file;
+ croak( "File '$file' does not exist" ) unless -e $file;
+ croak( "'$file' is a directory, not a file" ) unless -f _;
+ croak( "Insufficient permissions to read '$file'" ) unless -r _;
+
+ # Slurp in the file
+ local $/ = undef;
+ local *CFG;
+ unless ( open( CFG, $file ) ) {
+ croak("Failed to open file '$file': $!");
+ }
+ my $yaml = <CFG>;
+ unless ( close(CFG) ) {
+ croak("Failed to close file '$file': $!");
+ }
+
+ # Hand off to the actual parser
+ Load( $yaml );
+ }
+
+ # Parse a document from a string.
+ # Doing checks on $_[0] prevents us having to do a string copy.
+ sub Load ($) {
+ my $string = $_[0];
+ unless ( defined $string ) {
+ croak("Did not provide a string to load");
+ }
+
+ # Byte order marks
+ if ( $string =~ /^(?:\376\377|\377\376|\377\376\0\0|\0\0\376\377)/ ) {
+ croak("Stream has a non UTF-8 Unicode Byte Order Mark");
+ } else {
+ # Strip UTF-8 bom if found, we'll just ignore it
+ $string =~ s/^\357\273\277//;
+ }
+
+ # Try to decode as utf8
+ utf8::decode($string) if HAVE_UTF8;
+
+ # Check for some special cases
+ return () unless length $string;
+ unless ( $string =~ /[\012\015]+\z/ ) {
+ croak("Stream does not end with newline character");
+ }
+
+ # Split the file into lines
+ my @lines = grep { ! /^\s*(?:\#.*)?\z/ }
+ split /(?:\015{1,2}\012|\015|\012)/, $string;
+
+ # Strip the initial YAML header
+ @lines and $lines[0] =~ /^\%YAML[: ][\d\.]+.*\z/ and shift @lines;
+
+ # A nibbling parser
+ my @documents = ();
+ while ( @lines ) {
+ # Do we have a document header?
+ if ( $lines[0] =~ /^---\s*(?:(.+)\s*)?\z/ ) {
+ # Handle scalar documents
+ shift @lines;
+ if ( defined $1 and $1 !~ /^(?:\#.+|\%YAML[: ][\d\.]+)\z/ ) {
+ push @documents, _scalar( "$1", [ undef ], \@lines );
+ next;
+ }
+ }
+
+ if ( ! @lines or $lines[0] =~ /^(?:---|\.\.\.)/ ) {
+ # A naked document
+ push @documents, undef;
+ while ( @lines and $lines[0] !~ /^---/ ) {
+ shift @lines;
+ }
+
+ } elsif ( $lines[0] =~ /^\s*\-/ ) {
+ # An array at the root
+ my $document = [ ];
+ push @documents, $document;
+ _array( $document, [ 0 ], \@lines );
+
+ } elsif ( $lines[0] =~ /^(\s*)\S/ ) {
+ # A hash at the root
+ my $document = { };
+ push @documents, $document;
+ _hash( $document, [ length($1) ], \@lines );
+
+ } else {
+ croak("Parse::CPAN::Meta failed to classify line '$lines[0]'");
+ }
+ }
+
+ if ( wantarray ) {
+ return @documents;
+ } else {
+ return $documents[-1];
+ }
+ }
+
+ # Deparse a scalar string to the actual scalar
+ sub _scalar ($$$) {
+ my ($string, $indent, $lines) = @_;
+
+ # Trim trailing whitespace
+ $string =~ s/\s*\z//;
+
+ # Explitic null/undef
+ return undef if $string eq '~';
+
+ # Quotes
+ if ( $string =~ /^\'(.*?)\'\z/ ) {
+ return '' unless defined $1;
+ $string = $1;
+ $string =~ s/\'\'/\'/g;
+ return $string;
+ }
+ if ( $string =~ /^\"((?:\\.|[^\"])*)\"\z/ ) {
+ # Reusing the variable is a little ugly,
+ # but avoids a new variable and a string copy.
+ $string = $1;
+ $string =~ s/\\"/"/g;
+ $string =~ s/\\([never\\fartz]|x([0-9a-fA-F]{2}))/(length($1)>1)?pack("H2",$2):$UNESCAPES{$1}/gex;
+ return $string;
+ }
+
+ # Special cases
+ if ( $string =~ /^[\'\"!&]/ ) {
+ croak("Parse::CPAN::Meta does not support a feature in line '$lines->[0]'");
+ }
+ return {} if $string eq '{}';
+ return [] if $string eq '[]';
+
+ # Regular unquoted string
+ return $string unless $string =~ /^[>|]/;
+
+ # Error
+ croak("Parse::CPAN::Meta failed to find multi-line scalar content") unless @$lines;
+
+ # Check the indent depth
+ $lines->[0] =~ /^(\s*)/;
+ $indent->[-1] = length("$1");
+ if ( defined $indent->[-2] and $indent->[-1] <= $indent->[-2] ) {
+ croak("Parse::CPAN::Meta found bad indenting in line '$lines->[0]'");
+ }
+
+ # Pull the lines
+ my @multiline = ();
+ while ( @$lines ) {
+ $lines->[0] =~ /^(\s*)/;
+ last unless length($1) >= $indent->[-1];
+ push @multiline, substr(shift(@$lines), length($1));
+ }
+
+ my $j = (substr($string, 0, 1) eq '>') ? ' ' : "\n";
+ my $t = (substr($string, 1, 1) eq '-') ? '' : "\n";
+ return join( $j, @multiline ) . $t;
+ }
+
+ # Parse an array
+ sub _array ($$$) {
+ my ($array, $indent, $lines) = @_;
+
+ while ( @$lines ) {
+ # Check for a new document
+ if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
+ while ( @$lines and $lines->[0] !~ /^---/ ) {
+ shift @$lines;
+ }
+ return 1;
+ }
+
+ # Check the indent level
+ $lines->[0] =~ /^(\s*)/;
+ if ( length($1) < $indent->[-1] ) {
+ return 1;
+ } elsif ( length($1) > $indent->[-1] ) {
+ croak("Parse::CPAN::Meta found bad indenting in line '$lines->[0]'");
+ }
+
+ if ( $lines->[0] =~ /^(\s*\-\s+)[^\'\"]\S*\s*:(?:\s+|$)/ ) {
+ # Inline nested hash
+ my $indent2 = length("$1");
+ $lines->[0] =~ s/-/ /;
+ push @$array, { };
+ _hash( $array->[-1], [ @$indent, $indent2 ], $lines );
+
+ } elsif ( $lines->[0] =~ /^\s*\-(\s*)(.+?)\s*\z/ ) {
+ # Array entry with a value
+ shift @$lines;
+ push @$array, _scalar( "$2", [ @$indent, undef ], $lines );
+
+ } elsif ( $lines->[0] =~ /^\s*\-\s*\z/ ) {
+ shift @$lines;
+ unless ( @$lines ) {
+ push @$array, undef;
+ return 1;
+ }
+ if ( $lines->[0] =~ /^(\s*)\-/ ) {
+ my $indent2 = length("$1");
+ if ( $indent->[-1] == $indent2 ) {
+ # Null array entry
+ push @$array, undef;
+ } else {
+ # Naked indenter
+ push @$array, [ ];
+ _array( $array->[-1], [ @$indent, $indent2 ], $lines );
+ }
+
+ } elsif ( $lines->[0] =~ /^(\s*)\S/ ) {
+ push @$array, { };
+ _hash( $array->[-1], [ @$indent, length("$1") ], $lines );
+
+ } else {
+ croak("Parse::CPAN::Meta failed to classify line '$lines->[0]'");
+ }
+
+ } elsif ( defined $indent->[-2] and $indent->[-1] == $indent->[-2] ) {
+ # This is probably a structure like the following...
+ # ---
+ # foo:
+ # - list
+ # bar: value
+ #
+ # ... so lets return and let the hash parser handle it
+ return 1;
+
+ } else {
+ croak("Parse::CPAN::Meta failed to classify line '$lines->[0]'");
+ }
+ }
+
+ return 1;
+ }
+
+ # Parse an array
+ sub _hash ($$$) {
+ my ($hash, $indent, $lines) = @_;
+
+ while ( @$lines ) {
+ # Check for a new document
+ if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
+ while ( @$lines and $lines->[0] !~ /^---/ ) {
+ shift @$lines;
+ }
+ return 1;
+ }
+
+ # Check the indent level
+ $lines->[0] =~ /^(\s*)/;
+ if ( length($1) < $indent->[-1] ) {
+ return 1;
+ } elsif ( length($1) > $indent->[-1] ) {
+ croak("Parse::CPAN::Meta found bad indenting in line '$lines->[0]'");
+ }
+
+ # Get the key
+ unless ( $lines->[0] =~ s/^\s*([^\'\" ][^\n]*?)\s*:(\s+|$)// ) {
+ if ( $lines->[0] =~ /^\s*[?\'\"]/ ) {
+ croak("Parse::CPAN::Meta does not support a feature in line '$lines->[0]'");
+ }
+ croak("Parse::CPAN::Meta failed to classify line '$lines->[0]'");
+ }
+ my $key = $1;
+
+ # Do we have a value?
+ if ( length $lines->[0] ) {
+ # Yes
+ $hash->{$key} = _scalar( shift(@$lines), [ @$indent, undef ], $lines );
+ } else {
+ # An indent
+ shift @$lines;
+ unless ( @$lines ) {
+ $hash->{$key} = undef;
+ return 1;
+ }
+ if ( $lines->[0] =~ /^(\s*)-/ ) {
+ $hash->{$key} = [];
+ _array( $hash->{$key}, [ @$indent, length($1) ], $lines );
+ } elsif ( $lines->[0] =~ /^(\s*)./ ) {
+ my $indent2 = length("$1");
+ if ( $indent->[-1] >= $indent2 ) {
+ # Null hash entry
+ $hash->{$key} = undef;
+ } else {
+ $hash->{$key} = {};
+ _hash( $hash->{$key}, [ @$indent, length($1) ], $lines );
+ }
+ }
+ }
+ }
+
+ return 1;
+ }
+
+ 1;
+
+ __END__
+
+PARSE_CPAN_META
+
+$fatpacked{"lib/core/only.pm"} = <<'LIB_CORE_ONLY';
+ package lib::core::only;
+
+ use strict;
+ use warnings FATAL => 'all';
+ use Config;
+
+ sub import {
+ @INC = @Config{qw(privlibexp archlibexp)};
+ return
+ }
+
+ 1;
+LIB_CORE_ONLY
+
+$fatpacked{"local/lib.pm"} = <<'LOCAL_LIB';
+ use strict;
+ use warnings;
+
+ package local::lib;
+
+ use 5.008001; # probably works with earlier versions but I'm not supporting them
+ # (patches would, of course, be welcome)
+
+ use File::Spec ();
+ use File::Path ();
+ use Carp ();
+ use Config;
+
+ our $VERSION = '1.008001'; # 1.8.1
+
+ our @KNOWN_FLAGS = qw(--self-contained);
+
+ sub import {
+ my ($class, @args) = @_;
+
+ # Remember what PERL5LIB was when we started
+ my $perl5lib = $ENV{PERL5LIB} || '';
+
+ my %arg_store;
+ for my $arg (@args) {
+ # check for lethal dash first to stop processing before causing problems
+ if ($arg =~ /−/) {
+ die <<'DEATH';
+ WHOA THERE! It looks like you've got some fancy dashes in your commandline!
+ These are *not* the traditional -- dashes that software recognizes. You
+ probably got these by copy-pasting from the perldoc for this module as
+ rendered by a UTF8-capable formatter. This most typically happens on an OS X
+ terminal, but can happen elsewhere too. Please try again after replacing the
+ dashes with normal minus signs.
+ DEATH
+ }
+ elsif(grep { $arg eq $_ } @KNOWN_FLAGS) {
+ (my $flag = $arg) =~ s/--//;
+ $arg_store{$flag} = 1;
+ }
+ elsif($arg =~ /^--/) {
+ die "Unknown import argument: $arg";
+ }
+ else {
+ # assume that what's left is a path
+ $arg_store{path} = $arg;
+ }
+ }
+
+ if($arg_store{'self-contained'}) {
+ die "FATAL: The local::lib --self-contained flag has never worked reliably and the original author, Mark Stosberg, was unable or unwilling to maintain it. As such, this flag has been removed from the local::lib codebase in order to prevent misunderstandings and potentially broken builds. The local::lib authors recommend that you look at the lib::core::only module shipped with this distribution in order to create a more robust environment that is equivalent to what --self-contained provided (although quite possibly not what you originally thought it provided due to the poor quality of the documentation, for which we apologise).\n";
+ }
+
+ $arg_store{path} = $class->resolve_path($arg_store{path});
+ $class->setup_local_lib_for($arg_store{path});
+
+ for (@INC) { # Untaint @INC
+ next if ref; # Skip entry if it is an ARRAY, CODE, blessed, etc.
+ m/(.*)/ and $_ = $1;
+ }
+ }
+
+ sub pipeline;
+
+ sub pipeline {
+ my @methods = @_;
+ my $last = pop(@methods);
+ if (@methods) {
+ \sub {
+ my ($obj, @args) = @_;
+ $obj->${pipeline @methods}(
+ $obj->$last(@args)
+ );
+ };
+ } else {
+ \sub {
+ shift->$last(@_);
+ };
+ }
+ }
+
+ sub _uniq {
+ my %seen;
+ grep { ! $seen{$_}++ } @_;
+ }
+
+ sub resolve_path {
+ my ($class, $path) = @_;
+ $class->${pipeline qw(
+ resolve_relative_path
+ resolve_home_path
+ resolve_empty_path
+ )}($path);
+ }
+
+ sub resolve_empty_path {
+ my ($class, $path) = @_;
+ if (defined $path) {
+ $path;
+ } else {
+ '~/perl5';
+ }
+ }
+
+ sub resolve_home_path {
+ my ($class, $path) = @_;
+ return $path unless ($path =~ /^~/);
+ my ($user) = ($path =~ /^~([^\/]+)/); # can assume ^~ so undef for 'us'
+ my $tried_file_homedir;
+ my $homedir = do {
+ if (eval { require File::HomeDir } && $File::HomeDir::VERSION >= 0.65) {
+ $tried_file_homedir = 1;
+ if (defined $user) {
+ File::HomeDir->users_home($user);
+ } else {
+ File::HomeDir->my_home;
+ }
+ } else {
+ if (defined $user) {
+ (getpwnam $user)[7];
+ } else {
+ if (defined $ENV{HOME}) {
+ $ENV{HOME};
+ } else {
+ (getpwuid $<)[7];
+ }
+ }
+ }
+ };
+ unless (defined $homedir) {
+ Carp::croak(
+ "Couldn't resolve homedir for "
+ .(defined $user ? $user : 'current user')
+ .($tried_file_homedir ? '' : ' - consider installing File::HomeDir')
+ );
+ }
+ $path =~ s/^~[^\/]*/$homedir/;
+ $path;
+ }
+
+ sub resolve_relative_path {
+ my ($class, $path) = @_;
+ $path = File::Spec->rel2abs($path);
+ }
+
+ sub setup_local_lib_for {
+ my ($class, $path) = @_;
+ $path = $class->ensure_dir_structure_for($path);
+ if ($0 eq '-') {
+ $class->print_environment_vars_for($path);
+ exit 0;
+ } else {
+ $class->setup_env_hash_for($path);
+ @INC = _uniq(split($Config{path_sep}, $ENV{PERL5LIB}), @INC);
+ }
+ }
+
+ sub install_base_bin_path {
+ my ($class, $path) = @_;
+ File::Spec->catdir($path, 'bin');
+ }
+
+ sub install_base_perl_path {
+ my ($class, $path) = @_;
+ File::Spec->catdir($path, 'lib', 'perl5');
+ }
+
+ sub install_base_arch_path {
+ my ($class, $path) = @_;
+ File::Spec->catdir($class->install_base_perl_path($path), $Config{archname});
+ }
+
+ sub ensure_dir_structure_for {
+ my ($class, $path) = @_;
+ unless (-d $path) {
+ warn "Attempting to create directory ${path}\n";
+ }
+ File::Path::mkpath($path);
+ # Need to have the path exist to make a short name for it, so
+ # converting to a short name here.
+ $path = Win32::GetShortPathName($path) if $^O eq 'MSWin32';
+
+ return $path;
+ }
+
+ sub INTERPOLATE_ENV () { 1 }
+ sub LITERAL_ENV () { 0 }
+
+ sub guess_shelltype {
+ my $shellbin = 'sh';
+ if(defined $ENV{'SHELL'}) {
+ my @shell_bin_path_parts = File::Spec->splitpath($ENV{'SHELL'});
+ $shellbin = $shell_bin_path_parts[-1];
+ }
+ my $shelltype = do {
+ local $_ = $shellbin;
+ if(/csh/) {
+ 'csh'
+ } else {
+ 'bourne'
+ }
+ };
+
+ # Both Win32 and Cygwin have $ENV{COMSPEC} set.
+ if (defined $ENV{'COMSPEC'} && $^O ne 'cygwin') {
+ my @shell_bin_path_parts = File::Spec->splitpath($ENV{'COMSPEC'});
+ $shellbin = $shell_bin_path_parts[-1];
+ $shelltype = do {
+ local $_ = $shellbin;
+ if(/command\.com/) {
+ 'win32'
+ } elsif(/cmd\.exe/) {
+ 'win32'
+ } elsif(/4nt\.exe/) {
+ 'win32'
+ } else {
+ $shelltype
+ }
+ };
+ }
+ return $shelltype;
+ }
+
+ sub print_environment_vars_for {
+ my ($class, $path) = @_;
+ print $class->environment_vars_string_for($path);
+ }
+
+ sub environment_vars_string_for {
+ my ($class, $path) = @_;
+ my @envs = $class->build_environment_vars_for($path, LITERAL_ENV);
+ my $out = '';
+
+ # rather basic csh detection, goes on the assumption that something won't
+ # call itself csh unless it really is. also, default to bourne in the
+ # pathological situation where a user doesn't have $ENV{SHELL} defined.
+ # note also that shells with funny names, like zoid, are assumed to be
+ # bourne.
+
+ my $shelltype = $class->guess_shelltype;
+
+ while (@envs) {
+ my ($name, $value) = (shift(@envs), shift(@envs));
+ $value =~ s/(\\")/\\$1/g;
+ $out .= $class->${\"build_${shelltype}_env_declaration"}($name, $value);
+ }
+ return $out;
+ }
+
+ # simple routines that take two arguments: an %ENV key and a value. return
+ # strings that are suitable for passing directly to the relevant shell to set
+ # said key to said value.
+ sub build_bourne_env_declaration {
+ my $class = shift;
+ my($name, $value) = @_;
+ return qq{export ${name}="${value}"\n};
+ }
+
+ sub build_csh_env_declaration {
+ my $class = shift;
+ my($name, $value) = @_;
+ return qq{setenv ${name} "${value}"\n};
+ }
+
+ sub build_win32_env_declaration {
+ my $class = shift;
+ my($name, $value) = @_;
+ return qq{set ${name}=${value}\n};
+ }
+
+ sub setup_env_hash_for {
+ my ($class, $path) = @_;
+ my %envs = $class->build_environment_vars_for($path, INTERPOLATE_ENV);
+ @ENV{keys %envs} = values %envs;
+ }
+
+ sub build_environment_vars_for {
+ my ($class, $path, $interpolate) = @_;
+ return (
+ PERL_LOCAL_LIB_ROOT => $path,
+ PERL_MB_OPT => "--install_base ${path}",
+ PERL_MM_OPT => "INSTALL_BASE=${path}",
+ PERL5LIB => join($Config{path_sep},
+ $class->install_base_arch_path($path),
+ $class->install_base_perl_path($path),
+ (($ENV{PERL5LIB}||()) ?
+ ($interpolate == INTERPOLATE_ENV
+ ? ($ENV{PERL5LIB})
+ : (($^O ne 'MSWin32') ? '$PERL5LIB' : '%PERL5LIB%' ))
+ : ())
+ ),
+ PATH => join($Config{path_sep},
+ $class->install_base_bin_path($path),
+ ($interpolate == INTERPOLATE_ENV
+ ? ($ENV{PATH}||())
+ : (($^O ne 'MSWin32') ? '$PATH' : '%PATH%' ))
+ ),
+ )
+ }
+
+ 1;
+LOCAL_LIB
+
+$fatpacked{"version.pm"} = <<'VERSION';
+ #!perl -w
+ package version;
+
+ use 5.005_04;
+ use strict;
+
+ use vars qw(@ISA $VERSION $CLASS $STRICT $LAX *declare *qv);
+
+ $VERSION = 0.88;
+
+ $CLASS = 'version';
+
+ #--------------------------------------------------------------------------#
+ # Version regexp components
+ #--------------------------------------------------------------------------#
+
+ # Fraction part of a decimal version number. This is a common part of
+ # both strict and lax decimal versions
+
+ my $FRACTION_PART = qr/\.[0-9]+/;
+
+ # First part of either decimal or dotted-decimal strict version number.
+ # Unsigned integer with no leading zeroes (except for zero itself) to
+ # avoid confusion with octal.
+
+ my $STRICT_INTEGER_PART = qr/0|[1-9][0-9]*/;
+
+ # First part of either decimal or dotted-decimal lax version number.
+ # Unsigned integer, but allowing leading zeros. Always interpreted
+ # as decimal. However, some forms of the resulting syntax give odd
+ # results if used as ordinary Perl expressions, due to how perl treats
+ # octals. E.g.
+ # version->new("010" ) == 10
+ # version->new( 010 ) == 8
+ # version->new( 010.2) == 82 # "8" . "2"
+
+ my $LAX_INTEGER_PART = qr/[0-9]+/;
+
+ # Second and subsequent part of a strict dotted-decimal version number.
+ # Leading zeroes are permitted, and the number is always decimal.
+ # Limited to three digits to avoid overflow when converting to decimal
+ # form and also avoid problematic style with excessive leading zeroes.
+
+ my $STRICT_DOTTED_DECIMAL_PART = qr/\.[0-9]{1,3}/;
+
+ # Second and subsequent part of a lax dotted-decimal version number.
+ # Leading zeroes are permitted, and the number is always decimal. No
+ # limit on the numerical value or number of digits, so there is the
+ # possibility of overflow when converting to decimal form.
+
+ my $LAX_DOTTED_DECIMAL_PART = qr/\.[0-9]+/;
+
+ # Alpha suffix part of lax version number syntax. Acts like a
+ # dotted-decimal part.
+
+ my $LAX_ALPHA_PART = qr/_[0-9]+/;
+
+ #--------------------------------------------------------------------------#
+ # Strict version regexp definitions
+ #--------------------------------------------------------------------------#
+
+ # Strict decimal version number.
+
+ my $STRICT_DECIMAL_VERSION =
+ qr/ $STRICT_INTEGER_PART $FRACTION_PART? /x;
+
+ # Strict dotted-decimal version number. Must have both leading "v" and
+ # at least three parts, to avoid confusion with decimal syntax.
+
+ my $STRICT_DOTTED_DECIMAL_VERSION =
+ qr/ v $STRICT_INTEGER_PART $STRICT_DOTTED_DECIMAL_PART{2,} /x;
+
+ # Complete strict version number syntax -- should generally be used
+ # anchored: qr/ \A $STRICT \z /x
+
+ $STRICT =
+ qr/ $STRICT_DECIMAL_VERSION | $STRICT_DOTTED_DECIMAL_VERSION /x;
+
+ #--------------------------------------------------------------------------#
+ # Lax version regexp definitions
+ #--------------------------------------------------------------------------#
+
+ # Lax decimal version number. Just like the strict one except for
+ # allowing an alpha suffix or allowing a leading or trailing
+ # decimal-point
+
+ my $LAX_DECIMAL_VERSION =
+ qr/ $LAX_INTEGER_PART (?: \. | $FRACTION_PART $LAX_ALPHA_PART? )?
+ |
+ $FRACTION_PART $LAX_ALPHA_PART?
+ /x;
+
+ # Lax dotted-decimal version number. Distinguished by having either
+ # leading "v" or at least three non-alpha parts. Alpha part is only
+ # permitted if there are at least two non-alpha parts. Strangely
+ # enough, without the leading "v", Perl takes .1.2 to mean v0.1.2,
+ # so when there is no "v", the leading part is optional
+
+ my $LAX_DOTTED_DECIMAL_VERSION =
+ qr/
+ v $LAX_INTEGER_PART (?: $LAX_DOTTED_DECIMAL_PART+ $LAX_ALPHA_PART? )?
+ |
+ $LAX_INTEGER_PART? $LAX_DOTTED_DECIMAL_PART{2,} $LAX_ALPHA_PART?
+ /x;
+
+ # Complete lax version number syntax -- should generally be used
+ # anchored: qr/ \A $LAX \z /x
+ #
+ # The string 'undef' is a special case to make for easier handling
+ # of return values from ExtUtils::MM->parse_version
+
+ $LAX =
+ qr/ undef | $LAX_DECIMAL_VERSION | $LAX_DOTTED_DECIMAL_VERSION /x;
+
+ #--------------------------------------------------------------------------#
+
+ eval "use version::vxs $VERSION";
+ if ( $@ ) { # don't have the XS version installed
+ eval "use version::vpp $VERSION"; # don't tempt fate
+ die "$@" if ( $@ );
+ push @ISA, "version::vpp";
+ local $^W;
+ *version::qv = \&version::vpp::qv;
+ *version::declare = \&version::vpp::declare;
+ *version::_VERSION = \&version::vpp::_VERSION;
+ if ($] >= 5.009000 && $] < 5.011004) {
+ no strict 'refs';
+ *version::stringify = \&version::vpp::stringify;
+ *{'version::(""'} = \&version::vpp::stringify;
+ *version::new = \&version::vpp::new;
+ *version::parse = \&version::vpp::parse;
+ }
+ }
+ else { # use XS module
+ push @ISA, "version::vxs";
+ local $^W;
+ *version::declare = \&version::vxs::declare;
+ *version::qv = \&version::vxs::qv;
+ *version::_VERSION = \&version::vxs::_VERSION;
+ *version::vcmp = \&version::vxs::VCMP;
+ if ($] >= 5.009000 && $] < 5.011004) {
+ no strict 'refs';
+ *version::stringify = \&version::vxs::stringify;
+ *{'version::(""'} = \&version::vxs::stringify;
+ *version::new = \&version::vxs::new;
+ *version::parse = \&version::vxs::parse;
+ }
+
+ }
+
+ # Preloaded methods go here.
+ sub import {
+ no strict 'refs';
+ my ($class) = shift;
+
+ # Set up any derived class
+ unless ($class eq 'version') {
+ local $^W;
+ *{$class.'::declare'} = \&version::declare;
+ *{$class.'::qv'} = \&version::qv;
+ }
+
+ my %args;
+ if (@_) { # any remaining terms are arguments
+ map { $args{$_} = 1 } @_
+ }
+ else { # no parameters at all on use line
+ %args =
+ (
+ qv => 1,
+ 'UNIVERSAL::VERSION' => 1,
+ );
+ }
+
+ my $callpkg = caller();
+
+ if (exists($args{declare})) {
+ *{$callpkg.'::declare'} =
+ sub {return $class->declare(shift) }
+ unless defined(&{$callpkg.'::declare'});
+ }
+
+ if (exists($args{qv})) {
+ *{$callpkg.'::qv'} =
+ sub {return $class->qv(shift) }
+ unless defined(&{$callpkg.'::qv'});
+ }
+
+ if (exists($args{'UNIVERSAL::VERSION'})) {
+ local $^W;
+ *UNIVERSAL::VERSION
+ = \&version::_VERSION;
+ }
+
+ if (exists($args{'VERSION'})) {
+ *{$callpkg.'::VERSION'} = \&version::_VERSION;
+ }
+
+ if (exists($args{'is_strict'})) {
+ *{$callpkg.'::is_strict'} = \&version::is_strict
+ unless defined(&{$callpkg.'::is_strict'});
+ }
+
+ if (exists($args{'is_lax'})) {
+ *{$callpkg.'::is_lax'} = \&version::is_lax
+ unless defined(&{$callpkg.'::is_lax'});
+ }
+ }
+
+ sub is_strict { defined $_[0] && $_[0] =~ qr/ \A $STRICT \z /x }
+ sub is_lax { defined $_[0] && $_[0] =~ qr/ \A $LAX \z /x }
+
+ 1;
+VERSION
+
+$fatpacked{"version/vpp.pm"} = <<'VERSION_VPP';
+ package charstar;
+ # a little helper class to emulate C char* semantics in Perl
+ # so that prescan_version can use the same code as in C
+
+ use overload (
+ '""' => \&thischar,
+ '0+' => \&thischar,
+ '++' => \&increment,
+ '--' => \&decrement,
+ '+' => \&plus,
+ '-' => \&minus,
+ '*' => \&multiply,
+ 'cmp' => \&cmp,
+ '<=>' => \&spaceship,
+ 'bool' => \&thischar,
+ '=' => \&clone,
+ );
+
+ sub new {
+ my ($self, $string) = @_;
+ my $class = ref($self) || $self;
+
+ my $obj = {
+ string => [split(//,$string)],
+ current => 0,
+ };
+ return bless $obj, $class;
+ }
+
+ sub thischar {
+ my ($self) = @_;
+ my $last = $#{$self->{string}};
+ my $curr = $self->{current};
+ if ($curr >= 0 && $curr <= $last) {
+ return $self->{string}->[$curr];
+ }
+ else {
+ return '';
+ }
+ }
+
+ sub increment {
+ my ($self) = @_;
+ $self->{current}++;
+ }
+
+ sub decrement {
+ my ($self) = @_;
+ $self->{current}--;
+ }
+
+ sub plus {
+ my ($self, $offset) = @_;
+ my $rself = $self->clone;
+ $rself->{current} += $offset;
+ return $rself;
+ }
+
+ sub minus {
+ my ($self, $offset) = @_;
+ my $rself = $self->clone;
+ $rself->{current} -= $offset;
+ return $rself;
+ }
+
+ sub multiply {
+ my ($left, $right, $swapped) = @_;
+ my $char = $left->thischar();
+ return $char * $right;
+ }
+
+ sub spaceship {
+ my ($left, $right, $swapped) = @_;
+ unless (ref($right)) { # not an object already
+ $right = $left->new($right);
+ }
+ return $left->{current} <=> $right->{current};
+ }
+
+ sub cmp {
+ my ($left, $right, $swapped) = @_;
+ unless (ref($right)) { # not an object already
+ if (length($right) == 1) { # comparing single character only
+ return $left->thischar cmp $right;
+ }
+ $right = $left->new($right);
+ }
+ return $left->currstr cmp $right->currstr;
+ }
+
+ sub bool {
+ my ($self) = @_;
+ my $char = $self->thischar;
+ return ($char ne '');
+ }
+
+ sub clone {
+ my ($left, $right, $swapped) = @_;
+ $right = {
+ string => [@{$left->{string}}],
+ current => $left->{current},
+ };
+ return bless $right, ref($left);
+ }
+
+ sub currstr {
+ my ($self, $s) = @_;
+ my $curr = $self->{current};
+ my $last = $#{$self->{string}};
+ if (defined($s) && $s->{current} < $last) {
+ $last = $s->{current};
+ }
+
+ my $string = join('', @{$self->{string}}[$curr..$last]);
+ return $string;
+ }
+
+ package version::vpp;
+ use strict;
+
+ use POSIX qw/locale_h/;
+ use locale;
+ use vars qw ($VERSION @ISA @REGEXS);
+ $VERSION = 0.88;
+
+ use overload (
+ '""' => \&stringify,
+ '0+' => \&numify,
+ 'cmp' => \&vcmp,
+ '<=>' => \&vcmp,
+ 'bool' => \&vbool,
+ 'nomethod' => \&vnoop,
+ );
+
+ eval "use warnings";
+ if ($@) {
+ eval '
+ package warnings;
+ sub enabled {return $^W;}
+ 1;
+ ';
+ }
+
+ my $VERSION_MAX = 0x7FFFFFFF;
+
+ # implement prescan_version as closely to the C version as possible
+ use constant TRUE => 1;
+ use constant FALSE => 0;
+
+ sub isDIGIT {
+ my ($char) = shift->thischar();
+ return ($char =~ /\d/);
+ }
+
+ sub isALPHA {
+ my ($char) = shift->thischar();
+ return ($char =~ /[a-zA-Z]/);
+ }
+
+ sub isSPACE {
+ my ($char) = shift->thischar();
+ return ($char =~ /\s/);
+ }
+
+ sub BADVERSION {
+ my ($s, $errstr, $error) = @_;
+ if ($errstr) {
+ $$errstr = $error;
+ }
+ return $s;
+ }
+
+ sub prescan_version {
+ my ($s, $strict, $errstr, $sqv, $ssaw_decimal, $swidth, $salpha) = @_;
+ my $qv = defined $sqv ? $$sqv : FALSE;
+ my $saw_decimal = defined $ssaw_decimal ? $$ssaw_decimal : 0;
+ my $width = defined $swidth ? $$swidth : 3;
+ my $alpha = defined $salpha ? $$salpha : FALSE;
+
+ my $d = $s;
+
+ if ($qv && isDIGIT($d)) {
+ goto dotted_decimal_version;
+ }
+
+ if ($d eq 'v') { # explicit v-string
+ $d++;
+ if (isDIGIT($d)) {
+ $qv = TRUE;
+ }
+ else { # degenerate v-string
+ # requires v1.2.3
+ return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
+ }
+
+ dotted_decimal_version:
+ if ($strict && $d eq '0' && isDIGIT($d+1)) {
+ # no leading zeros allowed
+ return BADVERSION($s,$errstr,"Invalid version format (no leading zeros)");
+ }
+
+ while (isDIGIT($d)) { # integer part
+ $d++;
+ }
+
+ if ($d eq '.')
+ {
+ $saw_decimal++;
+ $d++; # decimal point
+ }
+ else
+ {
+ if ($strict) {
+ # require v1.2.3
+ return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
+ }
+ else {
+ goto version_prescan_finish;
+ }
+ }
+
+ {
+ my $i = 0;
+ my $j = 0;
+ while (isDIGIT($d)) { # just keep reading
+ $i++;
+ while (isDIGIT($d)) {
+ $d++; $j++;
+ # maximum 3 digits between decimal
+ if ($strict && $j > 3) {
+ return BADVERSION($s,$errstr,"Invalid version format (maximum 3 digits between decimals)");
+ }
+ }
+ if ($d eq '_') {
+ if ($strict) {
+ return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
+ }
+ if ( $alpha ) {
+ return BADVERSION($s,$errstr,"Invalid version format (multiple underscores)");
+ }
+ $d++;
+ $alpha = TRUE;
+ }
+ elsif ($d eq '.') {
+ if ($alpha) {
+ return BADVERSION($s,$errstr,"Invalid version format (underscores before decimal)");
+ }
+ $saw_decimal++;
+ $d++;
+ }
+ elsif (!isDIGIT($d)) {
+ last;
+ }
+ $j = 0;
+ }
+
+ if ($strict && $i < 2) {
+ # requires v1.2.3
+ return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
+ }
+ }
+ } # end if dotted-decimal
+ else
+ { # decimal versions
+ # special $strict case for leading '.' or '0'
+ if ($strict) {
+ if ($d eq '.') {
+ return BADVERSION($s,$errstr,"Invalid version format (0 before decimal required)");
+ }
+ if ($d eq '0' && isDIGIT($d+1)) {
+ return BADVERSION($s,$errstr,"Invalid version format (no leading zeros)");
+ }
+ }
+
+ # consume all of the integer part
+ while (isDIGIT($d)) {
+ $d++;
+ }
+
+ # look for a fractional part
+ if ($d eq '.') {
+ # we found it, so consume it
+ $saw_decimal++;
+ $d++;
+ }
+ elsif (!$d || $d eq ';' || isSPACE($d) || $d eq '}') {
+ if ( $d == $s ) {
+ # found nothing
+ return BADVERSION($s,$errstr,"Invalid version format (version required)");
+ }
+ # found just an integer
+ goto version_prescan_finish;
+ }
+ elsif ( $d == $s ) {
+ # didn't find either integer or period
+ return BADVERSION($s,$errstr,"Invalid version format (non-numeric data)");
+ }
+ elsif ($d eq '_') {
+ # underscore can't come after integer part
+ if ($strict) {
+ return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
+ }
+ elsif (isDIGIT($d+1)) {
+ return BADVERSION($s,$errstr,"Invalid version format (alpha without decimal)");
+ }
+ else {
+ return BADVERSION($s,$errstr,"Invalid version format (misplaced underscore)");
+ }
+ }
+ elsif ($d) {
+ # anything else after integer part is just invalid data
+ return BADVERSION($s,$errstr,"Invalid version format (non-numeric data)");
+ }
+
+ # scan the fractional part after the decimal point
+ if ($d && !isDIGIT($d) && ($strict || ! ($d eq ';' || isSPACE($d) || $d eq '}') )) {
+ # $strict or lax-but-not-the-end
+ return BADVERSION($s,$errstr,"Invalid version format (fractional part required)");
+ }
+
+ while (isDIGIT($d)) {
+ $d++;
+ if ($d eq '.' && isDIGIT($d-1)) {
+ if ($alpha) {
+ return BADVERSION($s,$errstr,"Invalid version format (underscores before decimal)");
+ }
+ if ($strict) {
+ return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions must begin with 'v')");
+ }
+ $d = $s; # start all over again
+ $qv = TRUE;
+ goto dotted_decimal_version;
+ }
+ if ($d eq '_') {
+ if ($strict) {
+ return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
+ }
+ if ( $alpha ) {
+ return BADVERSION($s,$errstr,"Invalid version format (multiple underscores)");
+ }
+ if ( ! isDIGIT($d+1) ) {
+ return BADVERSION($s,$errstr,"Invalid version format (misplaced underscore)");
+ }
+ $d++;
+ $alpha = TRUE;
+ }
+ }
+ }
+
+ version_prescan_finish:
+ while (isSPACE($d)) {
+ $d++;
+ }
+
+ if ($d && !isDIGIT($d) && (! ($d eq ';' || $d eq '}') )) {
+ # trailing non-numeric data
+ return BADVERSION($s,$errstr,"Invalid version format (non-numeric data)");
+ }
+
+ if (defined $sqv) {
+ $$sqv = $qv;
+ }
+ if (defined $swidth) {
+ $$swidth = $width;
+ }
+ if (defined $ssaw_decimal) {
+ $$ssaw_decimal = $saw_decimal;
+ }
+ if (defined $salpha) {
+ $$salpha = $alpha;
+ }
+ return $d;
+ }
+
+ sub scan_version {
+ my ($s, $rv, $qv) = @_;
+ my $start;
+ my $pos;
+ my $last;
+ my $errstr;
+ my $saw_decimal = 0;
+ my $width = 3;
+ my $alpha = FALSE;
+ my $vinf = FALSE;
+ my @av;
+
+ $s = new charstar $s;
+
+ while (isSPACE($s)) { # leading whitespace is OK
+ $s++;
+ }
+
+ $last = prescan_version($s, FALSE, \$errstr, \$qv, \$saw_decimal,
+ \$width, \$alpha);
+
+ if ($errstr) {
+ # 'undef' is a special case and not an error
+ if ( $s ne 'undef') {
+ use Carp;
+ Carp::croak($errstr);
+ }
+ }
+
+ $start = $s;
+ if ($s eq 'v') {
+ $s++;
+ }
+ $pos = $s;
+
+ if ( $qv ) {
+ $$rv->{qv} = $qv;
+ }
+ if ( $alpha ) {
+ $$rv->{alpha} = $alpha;
+ }
+ if ( !$qv && $width < 3 ) {
+ $$rv->{width} = $width;
+ }
+
+ while (isDIGIT($pos)) {
+ $pos++;
+ }
+ if (!isALPHA($pos)) {
+ my $rev;
+
+ for (;;) {
+ $rev = 0;
+ {
+ # this is atoi() that delimits on underscores
+ my $end = $pos;
+ my $mult = 1;
+ my $orev;
+
+ # the following if() will only be true after the decimal
+ # point of a version originally created with a bare
+ # floating point number, i.e. not quoted in any way
+ #
+ if ( !$qv && $s > $start && $saw_decimal == 1 ) {
+ $mult *= 100;
+ while ( $s < $end ) {
+ $orev = $rev;
+ $rev += $s * $mult;
+ $mult /= 10;
+ if ( (abs($orev) > abs($rev))
+ || (abs($rev) > $VERSION_MAX )) {
+ warn("Integer overflow in version %d",
+ $VERSION_MAX);
+ $s = $end - 1;
+ $rev = $VERSION_MAX;
+ $vinf = 1;
+ }
+ $s++;
+ if ( $s eq '_' ) {
+ $s++;
+ }
+ }
+ }
+ else {
+ while (--$end >= $s) {
+ $orev = $rev;
+ $rev += $end * $mult;
+ $mult *= 10;
+ if ( (abs($orev) > abs($rev))
+ || (abs($rev) > $VERSION_MAX )) {
+ warn("Integer overflow in version");
+ $end = $s - 1;
+ $rev = $VERSION_MAX;
+ $vinf = 1;
+ }
+ }
+ }
+ }
+
+ # Append revision
+ push @av, $rev;
+ if ( $vinf ) {
+ $s = $last;
+ last;
+ }
+ elsif ( $pos eq '.' ) {
+ $s = ++$pos;
+ }
+ elsif ( $pos eq '_' && isDIGIT($pos+1) ) {
+ $s = ++$pos;
+ }
+ elsif ( $pos eq ',' && isDIGIT($pos+1) ) {
+ $s = ++$pos;
+ }
+ elsif ( isDIGIT($pos) ) {
+ $s = $pos;
+ }
+ else {
+ $s = $pos;
+ last;
+ }
+ if ( $qv ) {
+ while ( isDIGIT($pos) ) {
+ $pos++;
+ }
+ }
+ else {
+ my $digits = 0;
+ while ( ( isDIGIT($pos) || $pos eq '_' ) && $digits < 3 ) {
+ if ( $pos ne '_' ) {
+ $digits++;
+ }
+ $pos++;
+ }
+ }
+ }
+ }
+ if ( $qv ) { # quoted versions always get at least three terms
+ my $len = $#av;
+ # This for loop appears to trigger a compiler bug on OS X, as it
+ # loops infinitely. Yes, len is negative. No, it makes no sense.
+ # Compiler in question is:
+ # gcc version 3.3 20030304 (Apple Computer, Inc. build 1640)
+ # for ( len = 2 - len; len > 0; len-- )
+ # av_push(MUTABLE_AV(sv), newSViv(0));
+ #
+ $len = 2 - $len;
+ while ($len-- > 0) {
+ push @av, 0;
+ }
+ }
+
+ # need to save off the current version string for later
+ if ( $vinf ) {
+ $$rv->{original} = "v.Inf";
+ $$rv->{vinf} = 1;
+ }
+ elsif ( $s > $start ) {
+ $$rv->{original} = $start->currstr($s);
+ if ( $qv && $saw_decimal == 1 && $start ne 'v' ) {
+ # need to insert a v to be consistent
+ $$rv->{original} = 'v' . $$rv->{original};
+ }
+ }
+ else {
+ $$rv->{original} = '0';
+ push(@av, 0);
+ }
+
+ # And finally, store the AV in the hash
+ $$rv->{version} = \@av;
+
+ # fix RT#19517 - special case 'undef' as string
+ if ($s eq 'undef') {
+ $s += 5;
+ }
+
+ return $s;
+ }
+
+ sub new
+ {
+ my ($class, $value) = @_;
+ my $self = bless ({}, ref ($class) || $class);
+ my $qv = FALSE;
+
+ if ( ref($value) && eval('$value->isa("version")') ) {
+ # Can copy the elements directly
+ $self->{version} = [ @{$value->{version} } ];
+ $self->{qv} = 1 if $value->{qv};
+ $self->{alpha} = 1 if $value->{alpha};
+ $self->{original} = ''.$value->{original};
+ return $self;
+ }
+
+ my $currlocale = setlocale(LC_ALL);
+
+ # if the current locale uses commas for decimal points, we
+ # just replace commas with decimal places, rather than changing
+ # locales
+ if ( localeconv()->{decimal_point} eq ',' ) {
+ $value =~ tr/,/./;
+ }
+
+ if ( not defined $value or $value =~ /^undef$/ ) {
+ # RT #19517 - special case for undef comparison
+ # or someone forgot to pass a value
+ push @{$self->{version}}, 0;
+ $self->{original} = "0";
+ return ($self);
+ }
+
+ if ( $#_ == 2 ) { # must be CVS-style
+ $value = $_[2];
+ $qv = TRUE;
+ }
+
+ $value = _un_vstring($value);
+
+ # exponential notation
+ if ( $value =~ /\d+.?\d*e[-+]?\d+/ ) {
+ $value = sprintf("%.9f",$value);
+ $value =~ s/(0+)$//; # trim trailing zeros
+ }
+
+ my $s = scan_version($value, \$self, $qv);
+
+ if ($s) { # must be something left over
+ warn("Version string '%s' contains invalid data; "
+ ."ignoring: '%s'", $value, $s);
+ }
+
+ return ($self);
+ }
+
+ *parse = \&new;
+
+ sub numify
+ {
+ my ($self) = @_;
+ unless (_verify($self)) {
+ require Carp;
+ Carp::croak("Invalid version object");
+ }
+ my $width = $self->{width} || 3;
+ my $alpha = $self->{alpha} || "";
+ my $len = $#{$self->{version}};
+ my $digit = $self->{version}[0];
+ my $string = sprintf("%d.", $digit );
+
+ for ( my $i = 1 ; $i < $len ; $i++ ) {
+ $digit = $self->{version}[$i];
+ if ( $width < 3 ) {
+ my $denom = 10**(3-$width);
+ my $quot = int($digit/$denom);
+ my $rem = $digit - ($quot * $denom);
+ $string .= sprintf("%0".$width."d_%d", $quot, $rem);
+ }
+ else {
+ $string .= sprintf("%03d", $digit);
+ }
+ }
+
+ if ( $len > 0 ) {
+ $digit = $self->{version}[$len];
+ if ( $alpha && $width == 3 ) {
+ $string .= "_";
+ }
+ $string .= sprintf("%0".$width."d", $digit);
+ }
+ else # $len = 0
+ {
+ $string .= sprintf("000");
+ }
+
+ return $string;
+ }
+
+ sub normal
+ {
+ my ($self) = @_;
+ unless (_verify($self)) {
+ require Carp;
+ Carp::croak("Invalid version object");
+ }
+ my $alpha = $self->{alpha} || "";
+ my $len = $#{$self->{version}};
+ my $digit = $self->{version}[0];
+ my $string = sprintf("v%d", $digit );
+
+ for ( my $i = 1 ; $i < $len ; $i++ ) {
+ $digit = $self->{version}[$i];
+ $string .= sprintf(".%d", $digit);
+ }
+
+ if ( $len > 0 ) {
+ $digit = $self->{version}[$len];
+ if ( $alpha ) {
+ $string .= sprintf("_%0d", $digit);
+ }
+ else {
+ $string .= sprintf(".%0d", $digit);
+ }
+ }
+
+ if ( $len <= 2 ) {
+ for ( $len = 2 - $len; $len != 0; $len-- ) {
+ $string .= sprintf(".%0d", 0);
+ }
+ }
+
+ return $string;
+ }
+
+ sub stringify
+ {
+ my ($self) = @_;
+ unless (_verify($self)) {
+ require Carp;
+ Carp::croak("Invalid version object");
+ }
+ return exists $self->{original}
+ ? $self->{original}
+ : exists $self->{qv}
+ ? $self->normal
+ : $self->numify;
+ }
+
+ sub vcmp
+ {
+ require UNIVERSAL;
+ my ($left,$right,$swap) = @_;
+ my $class = ref($left);
+ unless ( UNIVERSAL::isa($right, $class) ) {
+ $right = $class->new($right);
+ }
+
+ if ( $swap ) {
+ ($left, $right) = ($right, $left);
+ }
+ unless (_verify($left)) {
+ require Carp;
+ Carp::croak("Invalid version object");
+ }
+ unless (_verify($right)) {
+ require Carp;
+ Carp::croak("Invalid version object");
+ }
+ my $l = $#{$left->{version}};
+ my $r = $#{$right->{version}};
+ my $m = $l < $r ? $l : $r;
+ my $lalpha = $left->is_alpha;
+ my $ralpha = $right->is_alpha;
+ my $retval = 0;
+ my $i = 0;
+ while ( $i <= $m && $retval == 0 ) {
+ $retval = $left->{version}[$i] <=> $right->{version}[$i];
+ $i++;
+ }
+
+ # tiebreaker for alpha with identical terms
+ if ( $retval == 0
+ && $l == $r
+ && $left->{version}[$m] == $right->{version}[$m]
+ && ( $lalpha || $ralpha ) ) {
+
+ if ( $lalpha && !$ralpha ) {
+ $retval = -1;
+ }
+ elsif ( $ralpha && !$lalpha) {
+ $retval = +1;
+ }
+ }
+
+ # possible match except for trailing 0's
+ if ( $retval == 0 && $l != $r ) {
+ if ( $l < $r ) {
+ while ( $i <= $r && $retval == 0 ) {
+ if ( $right->{version}[$i] != 0 ) {
+ $retval = -1; # not a match after all
+ }
+ $i++;
+ }
+ }
+ else {
+ while ( $i <= $l && $retval == 0 ) {
+ if ( $left->{version}[$i] != 0 ) {
+ $retval = +1; # not a match after all
+ }
+ $i++;
+ }
+ }
+ }
+
+ return $retval;
+ }
+
+ sub vbool {
+ my ($self) = @_;
+ return vcmp($self,$self->new("0"),1);
+ }
+
+ sub vnoop {
+ require Carp;
+ Carp::croak("operation not supported with version object");
+ }
+
+ sub is_alpha {
+ my ($self) = @_;
+ return (exists $self->{alpha});
+ }
+
+ sub qv {
+ my $value = shift;
+ my $class = 'version';
+ if (@_) {
+ $class = ref($value) || $value;
+ $value = shift;
+ }
+
+ $value = _un_vstring($value);
+ $value = 'v'.$value unless $value =~ /(^v|\d+\.\d+\.\d)/;
+ my $version = $class->new($value);
+ return $version;
+ }
+
+ *declare = \&qv;
+
+ sub is_qv {
+ my ($self) = @_;
+ return (exists $self->{qv});
+ }
+
+
+ sub _verify {
+ my ($self) = @_;
+ if ( ref($self)
+ && eval { exists $self->{version} }
+ && ref($self->{version}) eq 'ARRAY'
+ ) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+
+ sub _is_non_alphanumeric {
+ my $s = shift;
+ $s = new charstar $s;
+ while ($s) {
+ return 0 if isSPACE($s); # early out
+ return 1 unless (isALPHA($s) || isDIGIT($s) || $s =~ /[.-]/);
+ $s++;
+ }
+ return 0;
+ }
+
+ sub _un_vstring {
+ my $value = shift;
+ # may be a v-string
+ if ( length($value) >= 3 && $value !~ /[._]/
+ && _is_non_alphanumeric($value)) {
+ my $tvalue;
+ if ( $] ge 5.008_001 ) {
+ $tvalue = _find_magic_vstring($value);
+ $value = $tvalue if length $tvalue;
+ }
+ elsif ( $] ge 5.006_000 ) {
+ $tvalue = sprintf("v%vd",$value);
+ if ( $tvalue =~ /^v\d+(\.\d+){2,}$/ ) {
+ # must be a v-string
+ $value = $tvalue;
+ }
+ }
+ }
+ return $value;
+ }
+
+ sub _find_magic_vstring {
+ my $value = shift;
+ my $tvalue = '';
+ require B;
+ my $sv = B::svref_2object(\$value);
+ my $magic = ref($sv) eq 'B::PVMG' ? $sv->MAGIC : undef;
+ while ( $magic ) {
+ if ( $magic->TYPE eq 'V' ) {
+ $tvalue = $magic->PTR;
+ $tvalue =~ s/^v?(.+)$/v$1/;
+ last;
+ }
+ else {
+ $magic = $magic->MOREMAGIC;
+ }
+ }
+ return $tvalue;
+ }
+
+ sub _VERSION {
+ my ($obj, $req) = @_;
+ my $class = ref($obj) || $obj;
+
+ no strict 'refs';
+ if ( exists $INC{"$class.pm"} and not %{"$class\::"} and $] >= 5.008) {
+ # file but no package
+ require Carp;
+ Carp::croak( "$class defines neither package nor VERSION"
+ ."--version check failed");
+ }
+
+ my $version = eval "\$$class\::VERSION";
+ if ( defined $version ) {
+ local $^W if $] <= 5.008;
+ $version = version::vpp->new($version);
+ }
+
+ if ( defined $req ) {
+ unless ( defined $version ) {
+ require Carp;
+ my $msg = $] < 5.006
+ ? "$class version $req required--this is only version "
+ : "$class does not define \$$class\::VERSION"
+ ."--version check failed";
+
+ if ( $ENV{VERSION_DEBUG} ) {
+ Carp::confess($msg);
+ }
+ else {
+ Carp::croak($msg);
+ }
+ }
+
+ $req = version::vpp->new($req);
+
+ if ( $req > $version ) {
+ require Carp;
+ if ( $req->is_qv ) {
+ Carp::croak(
+ sprintf ("%s version %s required--".
+ "this is only version %s", $class,
+ $req->normal, $version->normal)
+ );
+ }
+ else {
+ Carp::croak(
+ sprintf ("%s version %s required--".
+ "this is only version %s", $class,
+ $req->stringify, $version->stringify)
+ );
+ }
+ }
+ }
+
+ return defined $version ? $version->stringify : undef;
+ }
+
+ 1; #this line is important and will help the module return a true value
+VERSION_VPP
+
+s/^ //mg for values %fatpacked;
+
+unshift @INC, sub {
+ if (my $fat = $fatpacked{$_[1]}) {
+ open my $fh, '<', \$fat
+ or die "FatPacker error loading $_[1] (could be a perl installation issue?)";
+ return $fh;
+ }
+ return
+};
+
+} # END OF FATPACK CODE
+
+use strict;
+use App::cpanminus::script;
+
+unless (caller) {
+ my $app = App::cpanminus::script->new;
+ $app->parse_options(@ARGV);
+ $app->doit or exit(1);
+}
+
+__END__
+
+=head1 NAME
+
+cpanm - get, unpack build and install modules from CPAN
+
+=head1 SYNOPSIS
+
+ cpanm Test::More # install Test::More
+ cpanm MIYAGAWA/Plack-0.99_05.tar.gz # full distribution path
+ cpanm http://example.org/LDS/CGI.pm-3.20.tar.gz # install from URL
+ cpanm ~/dists/MyCompany-Enterprise-1.00.tar.gz # install from a local file
+ cpanm --interactive Task::Kensho # Configure interactively
+ cpanm . # install from local directory
+ cpanm --installdeps . # install all the deps for the current directory
+ cpanm -L extlib Plack # install Plack and all non-core deps into extlib
+ cpanm --mirror http://cpan.cpantesters.org/ DBI # use the fast-syncing mirror
+ cpanm --scandeps Moose # See what modules will be installed for Moose
+
+=head1 COMMANDS
+
+=over 4
+
+=item -i, --install
+
+Installs the modules. This is a default behavior and this is just a
+compatibility option to make it work like L<cpan> or L<cpanp>.
+
+=item --self-upgrade
+
+Upgrades itself. It's just an alias for:
+
+ cpanm App::cpanminus
+
+=item --info
+
+Displays the distribution information in
+C<AUTHOR/Dist-Name-ver.tar.gz> format in the standard out.
+
+=item --installdeps
+
+Installs the dependencies of the target distribution but won't build
+itself. Handy if you want to try the application from a version
+controlled repository such as git.
+
+ cpanm --installdeps .
+
+=item --look
+
+Download and unpack the distribution and then open the directory with
+your shell. Handy to poke around the source code or do the manual
+testing.
+
+=item -h, --help
+
+Displays the help message.
+
+=item -V, --version
+
+Displays the version number.
+
+=back
+
+=head1 OPTIONS
+
+You can specify the default options in C<PERL_CPANM_OPT> environment variable.
+
+=over 4
+
+=item -f, --force
+
+Force install modules even when testing failed.
+
+=item -n, --notest
+
+Skip the testing of modules. Use this only when you just want to save
+time for installing hundreds of distributions to the same perl and
+architecture you've already tested to make sure it builds fine.
+
+Defaults to false, and you can say C<--no-notest> to override when it
+is set in the default options in C<PERL_CPANM_OPT>.
+
+=item -S, --sudo
+
+Switch to the root user with C<sudo> when installing modules. Use this
+if you want to install modules to the system perl include path.
+
+Defaults to false, and you can say C<--no-sudo> to override when it is
+set in the default options in C<PERL_CPANM_OPT>.
+
+=item -v, --verbose
+
+Makes the output verbose. It also enables the interactive
+configuration. (See --interactive)
+
+=item -q, --quiet
+
+Makes the output even more quiet than the default. It doesn't print
+anything to the STDERR.
+
+=item -l, --local-lib
+
+Sets the L<local::lib> compatible path to install modules to. You
+don't need to set this if you already configure the shell environment
+variables using L<local::lib>, but this can be used to override that
+as well.
+
+=item -L, --local-lib-contained
+
+Same with C<--local-lib> but when examining the dependencies, it
+assumes no non-core modules are installed on the system. It's handy if
+you want to bundle application dependencies in one directory so you
+can distribute to other machines.
+
+For instance,
+
+ cpanm -L extlib Plack
+
+would install Plack and all of its non-core dependencies into the
+directory C<extlib>, which can be loaded from your application with:
+
+ use local::lib '/path/to/extlib';
+
+=item --mirror
+
+Specifies the base URL for the CPAN mirror to use, such as
+C<http://cpan.cpantesters.org/> (you can omit the trailing slash). You
+can specify multiple mirror URLs by repeating the command line option.
+
+Defaults to C<http://search.cpan.org/CPAN> which is a geo location
+aware redirector.
+
+=item --mirror-only
+
+Download the mirror's 02packages.details.txt.gz index file instead of
+querying the CPAN Meta DB.
+
+Select this option if you are using a local mirror of CPAN, such as
+minicpan when you're offline, or your own CPAN index (a.k.a darkpan).
+
+B<Tip:> It might be useful if you name these mirror options with your
+shell aliases, like:
+
+ alias minicpanm='cpanm --mirror ~/minicpan --mirror-only'
+ alias darkpan='cpanm --mirror http://mycompany.example.com/DPAN --mirror-only'
+
+=item --prompt
+
+Prompts when a test fails so that you can skip, force install, retry
+or look in the shell to see what's going wrong. It also prompts when
+one of the dependency failed if you want to proceed the installation.
+
+Defaults to false, and you can say C<--no-prompt> to override if it's
+set in the default options in C<PERL_CPANM_OPT>.
+
+=item --reinstall
+
+cpanm, when given a module name in the command line (i.e. C<cpanm
+Plack>), checks the locally installed version first and skips if it is
+already installed. This option makes it skip the check, so:
+
+ cpanm --reinstall Plack
+
+would reinstall L<Plack> even if your locally installed version is
+latest, or even newer (which would happen if you install a developer
+release from version control repositories).
+
+Defaults to false.
+
+=item --interactive
+
+Makes the configuration (such as C<Makefile.PL> and C<Build.PL>)
+interactive, so you can answer questions in the distribution that
+requires custom configuration or Task:: distributions.
+
+Defaults to false, and you can say C<--no-interactive> to override
+when it's set in the default options in C<PERL_CPANM_OPT>.
+
+=item --scandeps
+
+Scans the depencencies of given modules and output the tree in a text
+format. (See C<--format> below for more options)
+
+Because this command doesn't actually install any distributions, it
+will be useful that by typing:
+
+ cpanm --scandeps Catalyst::Runtime
+
+you can make sure what modules will be installed.
+
+This command takes into account which modules you already have
+installed in your system. If you want to see what modules will be
+installed against a vanilla perl installation, you might want to
+combine it with C<-L> option.
+
+=item --format
+
+Determines what format to display the scanned dependency
+tree. Available options are C<tree>, C<json>, C<yaml> and C<dists>.
+
+=over 8
+
+=item tree
+
+Displays the tree in a plain text format. This is the default value.
+
+=item json, yaml
+
+Outputs the tree in a JSON or YAML format. L<JSON> and L<YAML> modules
+need to be installed respectively. The output tree is represented as a
+recursive tuple of:
+
+ [ distribution, dependencies ]
+
+and the container is an array containing the root elements. Note that
+there may be multiple root nodes, since you can give multiple modules
+to the C<--scandeps> command.
+
+=item dists
+
+C<dists> is a special output format, where it prints the distribution
+filename in the I<depth first order> after the dependency resolution,
+like:
+
+ GAAS/MIME-Base64-3.13.tar.gz
+ GAAS/URI-1.58.tar.gz
+ PETDANCE/HTML-Tagset-3.20.tar.gz
+ GAAS/HTML-Parser-3.68.tar.gz
+ GAAS/libwww-perl-5.837.tar.gz
+
+which means you can install these distributions in this order without
+extra dependencies. When combined with C<-L> option, it will be useful
+to replay installations on other machines.
+
+=back
+
+=item --save-dists
+
+Specifies the optional directory path to copy downloaded tarballs in
+the CPAN mirror compatible directory structure
+i.e. I<authors/id/A/AU/AUTHORS/Foo-Bar-version.tar.gz>
+
+=item --uninst-shadows
+
+Uninstalls the shadow files of the distribution that you're
+installing. This eliminates the confusion if you're trying to install
+core (dual-life) modules from CPAN against perl 5.10 or older, or
+modules that used to be XS-based but switched to pure perl at some
+version.
+
+If you run cpanm as root and use C<INSTALL_BASE> or equivalent to
+specify custom installation path, you SHOULD disable this option so
+you won't accidentally uninstall dual-life modules from the core
+include path.
+
+Defaults to true if your perl version is smaller than 5.12, and you
+can disable that with C<--no-uninst-shadows>.
+
+B<NOTE>: Since version 1.3000 this flag is turned off by default for
+perl newer than 5.12, since with 5.12 @INC contains site_perl directory
+I<before> the perl core library path, and uninstalling shadows is not
+necessary anymore and does more harm by deleting files from the core
+library path.
+
+=item --auto-cleanup
+
+Specifies the number of days in whcih cpanm's work directories
+expire. Defaults to 7, which means old work directories will be
+cleaned up in one week.
+
+You can set the value to C<0> to make cpan never cleanup those
+directories.
+
+=item --man-pages
+
+Generates man pages for executables (man1) and libraries (man3).
+
+Defaults to false (no man pages generated) if
+C<-L|--local-lib-contained> option is supplied. Otherwise, defaults to
+true, and you can disable it with C<--no-man-pages>.
+
+=item --lwp
+
+Uses L<LWP> module to download stuff over HTTP. Defaults to true, and
+you can say C<--no-lwp> to disable using LWP, when you want to upgrade
+LWP from CPAN on some broken perl systems.
+
+=item --wget
+
+Uses GNU Wget (if available) to download stuff. Defaults to true, and
+you can say C<--no-wget> to disable using Wget (versions of Wget older
+than 1.9 don't support the C<--retry-connrefused> option used by cpanm).
+
+=item --curl
+
+Uses cURL (if available) to download stuff. Defaults to true, and
+you can say C<--no-curl> to disable using cURL.
+
+Normally with C<--lwp>, C<--wget> and C<--curl> options set to true
+(which is the default) cpanm tries L<LWP>, Wget, cURL and L<HTTP::Tiny>
+(in that order) and uses the first one available.
+
+=back
+
+=head1 SEE ALSO
+
+L<App::cpanminus>
+
+=head1 COPYRIGHT
+
+Copyright 2010 Tatsuhiko Miyagawa.
+
+=head1 AUTHOR
+
+Tatsuhiko Miyagawa
+
+=cut
diff --git a/perl-external/bin/module-manage.pl b/perl-external/bin/module-manage.pl
new file mode 100755
index 000000000..5e826aabf
--- /dev/null
+++ b/perl-external/bin/module-manage.pl
@@ -0,0 +1,265 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use IPC::Run3;
+use LWP::Simple;
+use File::Slurp;
+use Path::Class;
+use List::MoreUtils 'uniq';
+
+# TODO - 'updates' action that lists packages that could be updated
+# TODO - add smarts to strip out old packages (could switch to building using files.txt after)
+
+my $root_dir = file(__FILE__)->dir->parent->absolute->stringify;
+my $module_list = "$root_dir/modules.txt";
+my $file_list = "$root_dir/files.txt";
+my $minicpan = "$root_dir/minicpan";
+my $local_packages_file = "$minicpan/modules/02packages.details.txt";
+my $local_packages_file_gz = "$local_packages_file.gz";
+
+my %actions = (
+ add => \&add,
+ build_all => \&build_all,
+ fetch_all => \&fetch_all,
+ force_install => \&force_install,
+ index_minicpan => \&index_minicpan,
+ init => \&init,
+ setup => \&setup,
+ sort_files => \&sort_files,
+ zap => \&zap,
+);
+
+# work out what to run
+my ( $action, @args ) = @ARGV;
+$actions{$action}
+ ? $actions{$action}->(@args)
+ : die("Usage: $0 action [...]\n actions: "
+ . join( ', ', sort keys %actions )
+ . "\n" );
+
+exit;
+
+############################################################################
+
+sub init {
+ add('App::cpanminus');
+}
+
+sub setup {
+ fetch_all();
+ build('App::cpanminus');
+ build_all();
+}
+
+sub add {
+ my $module = shift || die "Usage: $0 add Dist::To::Add";
+
+ # try to install the distribution using cpanm
+ my $out = '';
+ my $cmd = "cpanm --reinstall --save-dists $minicpan $module";
+
+ run3( $cmd, undef, undef, undef )
+ || die "Error running '$cmd'";
+
+ write_file( $module_list, { append => 1 }, "$module\n" );
+
+ index_minicpan();
+ sort_files();
+}
+
+sub index_minicpan {
+
+ # Go through all files in minicpan and add to files.txt
+ my @files = sort map { s{^.*?(/authors/id/.*)$}{$1}; $_ }
+ split '\s', `find $minicpan/authors -type f`;
+ write_file( $file_list, map { "$_\n" } @files );
+
+ # work out which ones are not currently in packages
+ my @local_packages_lines = read_packages_txt_gz($local_packages_file_gz);
+
+ # Are there any missing files?
+ my @missing_files = ();
+ MINICPAN_FILE:
+ foreach my $file (@files) {
+ my ($auth_and_file) = $file =~ m{/authors/id/./../(.*)$};
+
+ foreach my $line (@local_packages_lines) {
+ next MINICPAN_FILE if $line =~ m{$auth_and_file};
+ }
+
+ push @missing_files, $auth_and_file;
+ }
+
+ # If there are no missing files we can stop
+ return unless @missing_files;
+
+ # Fetch 02packages off live cpan
+ my $remote_packages_url =
+ 'http://cpan.perl.org/modules/02packages.details.txt.gz';
+ my $remote_packages_file = "$minicpan/modules/remote_packages.txt.gz";
+ print " Fetching '$remote_packages_url'...\n";
+ is_error( mirror( $remote_packages_url, $remote_packages_file ) )
+ && die "Could not retrieve '$remote_packages_url'";
+ print " done...\n";
+
+ my @remote_packages_lines = read_packages_txt_gz($remote_packages_file);
+
+ # Find remaining in live file and add to local file
+ my %lines_to_add = ();
+ foreach my $missing (@missing_files) {
+ print " Finding matches for '$missing'\n";
+ my @matches = grep { m{$missing} } @remote_packages_lines;
+ next unless @matches;
+ $lines_to_add{$missing} = \@matches;
+ }
+
+ # for packages still not found parse out the contents
+ foreach my $missing (@missing_files) {
+ next if $lines_to_add{$missing};
+
+ # do a require here so that this module does not prevent 'setup' from
+ # being run - needed to install it.
+ require CPAN::ParseDistribution;
+
+ print " Parsing out matches for '$missing'\n";
+
+ my ( $A, $B ) = $missing =~ m{^(.)(.)};
+ my $dist =
+ CPAN::ParseDistribution->new("$minicpan/authors/id/$A/$A$B/$missing");
+
+ my $modules = $dist->modules();
+ my @matches = ();
+
+ foreach my $module ( sort keys %$modules ) {
+ my $version = $modules->{$module} || 'undef';
+
+ # Zucchini 0.000017 C/CH/CHISEL/Zucchini-0.0.17.tar.gz
+ push @matches, "$module $version $A/$A$B/$missing\n";
+ }
+
+ $lines_to_add{$missing} = \@matches;
+ }
+
+ # combine and sort the lines found
+ my @new_lines = sort @local_packages_lines,
+ map { @$_ } values %lines_to_add;
+ unlink $local_packages_file_gz;
+ write_file( $local_packages_file, map { "$_\n" } packages_file_headers(),
+ @new_lines );
+ system "gzip -v $local_packages_file";
+}
+
+sub read_packages_txt_gz {
+ my $file = shift;
+
+ return unless -e $file;
+
+ my @lines = split /\n/, `zcat $file`;
+
+ # ditch the headers
+ while ( my $line = shift @lines ) {
+ last if $line =~ m{^\s*$};
+ }
+
+ return @lines;
+}
+
+sub packages_file_headers {
+
+ # this is all fake stuff
+
+ return << 'END_OF_LINES';
+Allow-Packages-Only-Once: 0
+Columns: package name, version, path
+Description: Package names for my private CPAN
+File: 02packages.details.txt
+Intended-For: My private CPAN
+Last-Updated: Wed, 04 May 2011 09:59:13 GMT
+Line-Count: 1389
+URL: http://example.com/MyCPAN/modules/02packages.details.txt
+Written-By: /home/evdb/fixmystreet/perl-external/local-lib/bin/dpan using CPAN::PackageDetails 0.25
+
+END_OF_LINES
+}
+
+sub build_all {
+ my @modules = sort uniq map { s{\s+$}{}; $_; } read_file($module_list);
+ build($_) for @modules;
+}
+
+sub build {
+ my $module = shift #
+ || die "Usage: $0 build Module::To::Build\n";
+
+ print " --- checking/installing $module ---\n";
+
+ my $out = '';
+ my $cmd = "cpanm --mirror $minicpan --mirror-only $module";
+
+ # print " running '$cmd'\n";
+
+ run3( $cmd, undef, \$out, \$out )
+ || die "Error running '$cmd'";
+
+ my @lines =
+ grep { m{\S} }
+ split /\n+/, $out;
+ my $last_line = $lines[-1];
+
+ die "Error building '$module':\n\n$last_line\n\n$out\n\n"
+ unless $last_line =~ m{Successfully installed }
+ || $last_line =~ m{is up to date}
+ || $last_line =~ m{\d+ distributions? installed};
+}
+
+sub fetch_all {
+ my @urls = sort uniq map { s{\s+$}{}; $_; } read_file($file_list);
+ fetch($_) for @urls;
+}
+
+sub fetch {
+ my $filename = shift;
+
+ my $destination = file("$minicpan/$filename");
+ $destination->dir->mkpath;
+
+ return if -e $destination;
+
+ # create a list of urls to try in order
+ my @urls = (
+ "http://search.cpan.org/CPAN" . $filename,
+ "http://backpan.perl.org" . $filename,
+ );
+
+ while ( scalar @urls ) {
+ my $url = shift @urls;
+
+ # try to fetch
+ print " Fetching '$url'...\n";
+ last if is_success( getstore( $url, "$destination" ) );
+
+ # if more options try again
+ next if scalar @urls;
+
+ # could not retrieve - die
+ die "ERROR - ran out of urls fetching '$filename'";
+ }
+}
+
+sub zap {
+
+ # delete all the bits that are generated
+ my $local_lib_root = $ENV{PERL_LOCAL_LIB_ROOT} || die;
+ dir($local_lib_root)->rmtree(1);
+ dir($minicpan)->subdir('authors')->rmtree(1);
+}
+
+sub sort_files {
+ foreach my $file ( $file_list, $module_list ) {
+ my @entries = read_file($file);
+ @entries = uniq sort @entries;
+ write_file( $file, @entries );
+ }
+}
diff --git a/perl-external/files.txt b/perl-external/files.txt
new file mode 100644
index 000000000..e43fc2005
--- /dev/null
+++ b/perl-external/files.txt
@@ -0,0 +1,252 @@
+/authors/id/A/AB/ABRAXXA/Catalyst-View-TT-0.36.tar.gz
+/authors/id/A/AB/ABRAXXA/DBIx-Class-0.08127.tar.gz
+/authors/id/A/AB/ABW/AppConfig-1.66.tar.gz
+/authors/id/A/AB/ABW/Template-Toolkit-2.22.tar.gz
+/authors/id/A/AC/ACALPINI/Lingua-Stem-It-0.02.tar.gz
+/authors/id/A/AC/ACOBURN/Lingua-EN-Tagger-0.16.tar.gz
+/authors/id/A/AD/ADAMK/Archive-Zip-1.30.tar.gz
+/authors/id/A/AD/ADAMK/Class-Inspector-1.25.tar.gz
+/authors/id/A/AD/ADAMK/DBD-SQLite-1.31.tar.gz
+/authors/id/A/AD/ADAMK/File-Remove-1.48.tar.gz
+/authors/id/A/AD/ADAMK/File-ShareDir-1.03.tar.gz
+/authors/id/A/AD/ADAMK/Module-Install-1.00.tar.gz
+/authors/id/A/AD/ADAMK/Object-Signature-1.05.tar.gz
+/authors/id/A/AD/ADAMK/PPI-1.215.tar.gz
+/authors/id/A/AD/ADAMK/Params-Util-1.03.tar.gz
+/authors/id/A/AD/ADAMK/Task-Weaken-1.04.tar.gz
+/authors/id/A/AD/ADAMK/Test-NoWarnings-1.02.tar.gz
+/authors/id/A/AD/ADAMK/Test-Object-0.07.tar.gz
+/authors/id/A/AD/ADAMK/Test-SubCalls-1.09.tar.gz
+/authors/id/A/AD/ADAMK/YAML-Tiny-1.48.tar.gz
+/authors/id/A/AD/ADIE/Test-Exception-0.31.tar.gz
+/authors/id/A/AL/ALEXP/Net-Domain-TLD-1.68.tar.gz
+/authors/id/A/AL/ALGDR/Lingua-Stem-Ru-0.01.tar.gz
+/authors/id/A/AN/ANDK/CPAN-Checksums-2.07.tar.gz
+/authors/id/A/AN/ANDYA/CGI-Simple-1.113.tar.gz
+/authors/id/A/AN/ANDYA/Test-Harness-3.23.tar.gz
+/authors/id/A/AR/ARJAY/Compress-Bzip2-2.09.tar.gz
+/authors/id/A/AS/ASKSH/Snowball-Norwegian-1.2.tar.gz
+/authors/id/A/AS/ASKSH/Snowball-Swedish-1.2.tar.gz
+/authors/id/A/AU/AUDREYT/Test-use-ok-0.02.tar.gz
+/authors/id/B/BD/BDFOY/CPAN-PackageDetails-0.25.tar.gz
+/authors/id/B/BD/BDFOY/ConfigReader-Simple-1.28.tar.gz
+/authors/id/B/BD/BDFOY/Distribution-Guess-BuildSystem-0.12.tar.gz
+/authors/id/B/BD/BDFOY/File-Find-Closures-1.09.tar.gz
+/authors/id/B/BD/BDFOY/Module-Extract-Namespaces-0.14.tar.gz
+/authors/id/B/BD/BDFOY/Module-Extract-Use-0.17.tar.gz
+/authors/id/B/BD/BDFOY/Module-Extract-VERSION-0.13.tar.gz
+/authors/id/B/BD/BDFOY/MyCPAN-App-DPAN-1.28.tar.gz
+/authors/id/B/BD/BDFOY/MyCPAN-Indexer-1.28.tar.gz
+/authors/id/B/BD/BDFOY/Test-Output-0.16.tar.gz
+/authors/id/B/BI/BINGOS/Archive-Tar-1.76.tar.gz
+/authors/id/B/BI/BINGOS/Module-CoreList-2.45.tar.gz
+/authors/id/B/BO/BOBTFISH/Catalyst-Action-RenderView-0.16.tar.gz
+/authors/id/B/BO/BOBTFISH/Catalyst-Authentication-Store-DBIx-Class-0.1401.tar.gz
+/authors/id/B/BO/BOBTFISH/Catalyst-Devel-1.31.tar.gz
+/authors/id/B/BO/BOBTFISH/Catalyst-Model-Adaptor-0.10.tar.gz
+/authors/id/B/BO/BOBTFISH/Catalyst-Plugin-Authentication-0.10017.tar.gz
+/authors/id/B/BO/BOBTFISH/Catalyst-Plugin-Session-0.31.tar.gz
+/authors/id/B/BO/BOBTFISH/Catalyst-Plugin-Session-Store-Delegate-0.06.tar.gz
+/authors/id/B/BO/BOBTFISH/Catalyst-Plugin-Unicode-0.93.tar.gz
+/authors/id/B/BO/BOBTFISH/Catalyst-Plugin-Unicode-Encoding-1.1.tar.gz
+/authors/id/B/BO/BOBTFISH/Catalyst-Runtime-5.80032.tar.gz
+/authors/id/B/BO/BOBTFISH/CatalystX-Component-Traits-0.16.tar.gz
+/authors/id/B/BO/BOBTFISH/MooseX-Getopt-0.35.tar.gz
+/authors/id/B/BO/BOBTFISH/Test-WWW-Mechanize-Catalyst-0.53.tar.gz
+/authors/id/B/BO/BOBTFISH/namespace-autoclean-0.12.tar.gz
+/authors/id/B/BP/BPOWERS/Memoize-ExpireLRU-0.55.tar.gz
+/authors/id/B/BR/BRICAS/Catalyst-Plugin-ConfigLoader-0.30.tar.gz
+/authors/id/B/BR/BRICAS/Config-Any-0.20.tar.gz
+/authors/id/C/CH/CHOCOLATE/Scope-Guard-0.20.tar.gz
+/authors/id/C/CH/CHORNY/Hook-LexWrap-0.24.tar.gz
+/authors/id/C/CH/CHORNY/Test-Warn-0.23.tar.gz
+/authors/id/C/CH/CHORNY/Tie-IxHash-1.22.tar.gz
+/authors/id/C/CI/CINE/Lingua-Stem-Snowball-Da-1.01.tar.gz
+/authors/id/C/CK/CKRAS/DateTime-Format-HTTP-0.40.tar.gz
+/authors/id/C/CO/COGENT/Tree-DAG_Node-1.06.tar.gz
+/authors/id/C/CO/CORION/parent-0.225.tar.gz
+/authors/id/C/CR/CRENZ/Module-Find-0.10.tar.gz
+/authors/id/D/DA/DAGOLDEN/CPAN-Meta-2.110580.tar.gz
+/authors/id/D/DA/DAGOLDEN/CPAN-Meta-YAML-0.003.tar.gz
+/authors/id/D/DA/DAGOLDEN/ExtUtils-CBuilder-0.280202.tar.gz
+/authors/id/D/DA/DAGOLDEN/ExtUtils-ParseXS-2.2206.tar.gz
+/authors/id/D/DA/DAGOLDEN/Module-Build-0.3800.tar.gz
+/authors/id/D/DA/DAGOLDEN/Module-Metadata-1.000004.tar.gz
+/authors/id/D/DA/DAGOLDEN/Parse-CPAN-Meta-1.4401.tar.gz
+/authors/id/D/DA/DAGOLDEN/Perl-OSType-1.002.tar.gz
+/authors/id/D/DA/DAGOLDEN/Sub-Uplevel-0.22.tar.gz
+/authors/id/D/DC/DCANTRELL/CPAN-ParseDistribution-1.3.tar.gz
+/authors/id/D/DC/DCANTRELL/Data-Compare-1.22.tar.gz
+/authors/id/D/DC/DCONWAY/Lingua-EN-Inflect-1.893.tar.gz
+/authors/id/D/DL/DLAND/File-Path-2.08.tar.gz
+/authors/id/D/DL/DLUX/Parallel-ForkManager-0.7.9.tar.gz
+/authors/id/D/DM/DMAKI/DateTime-Format-Pg-0.16005.tar.gz
+/authors/id/D/DM/DMUEY/File-Copy-Recursive-0.38.tar.gz
+/authors/id/D/DM/DMUEY/Hash-Merge-0.12.tar.gz
+/authors/id/D/DO/DOY/Dist-CheckConflicts-0.02.tar.gz
+/authors/id/D/DO/DOY/Package-Stash-0.26.tar.gz
+/authors/id/D/DO/DOY/Package-Stash-XS-0.22.tar.gz
+/authors/id/D/DO/DOY/Try-Tiny-0.09.tar.gz
+/authors/id/D/DR/DROLSKY/Class-Factory-Util-1.7.tar.gz
+/authors/id/D/DR/DROLSKY/DateTime-0.70.tar.gz
+/authors/id/D/DR/DROLSKY/DateTime-Format-Builder-0.80.tar.gz
+/authors/id/D/DR/DROLSKY/DateTime-Format-Strptime-1.5000.tar.gz
+/authors/id/D/DR/DROLSKY/DateTime-Locale-0.45.tar.gz
+/authors/id/D/DR/DROLSKY/DateTime-TimeZone-1.34.tar.gz
+/authors/id/D/DR/DROLSKY/File-ChangeNotify-0.19.tar.gz
+/authors/id/D/DR/DROLSKY/File-Slurp-9999.13.tar.gz
+/authors/id/D/DR/DROLSKY/Moose-1.24.tar.gz
+/authors/id/D/DR/DROLSKY/MooseX-Params-Validate-0.16.tar.gz
+/authors/id/D/DR/DROLSKY/MooseX-SemiAffordanceAccessor-0.09.tar.gz
+/authors/id/D/DR/DROLSKY/MooseX-Types-0.25.tar.gz
+/authors/id/D/DR/DROLSKY/Package-DeprecationManager-0.10.tar.gz
+/authors/id/D/DR/DRTECH/Locale-Maketext-Lexicon-0.86.tar.gz
+/authors/id/F/FD/FDALY/Test-Tester-0.107.tar.gz
+/authors/id/F/FE/FERREIRA/Devel-Hide-0.0008.tar.gz
+/authors/id/F/FE/FERREIRA/Exporter-5.63.tar.gz
+/authors/id/F/FE/FERREIRA/Term-Size-Any-0.001.tar.gz
+/authors/id/F/FE/FERREIRA/Term-Size-Perl-0.029.tar.gz
+/authors/id/F/FL/FLORA/B-Hooks-EndOfScope-0.09.tar.gz
+/authors/id/F/FL/FLORA/Catalyst-Plugin-Session-Store-DBIC-0.11.tar.gz
+/authors/id/F/FL/FLORA/Class-C3-0.23.tar.gz
+/authors/id/F/FL/FLORA/Class-C3-Adopt-NEXT-0.13.tar.gz
+/authors/id/F/FL/FLORA/Class-MOP-1.12.tar.gz
+/authors/id/F/FL/FLORA/Data-Visitor-0.27.tar.gz
+/authors/id/F/FL/FLORA/Devel-GlobalDestruction-0.03.tar.gz
+/authors/id/F/FL/FLORA/HTTP-Request-AsCGI-1.2.tar.gz
+/authors/id/F/FL/FLORA/MRO-Compat-0.11.tar.gz
+/authors/id/F/FL/FLORA/MooseX-Emulate-Class-Accessor-Fast-0.00903.tar.gz
+/authors/id/F/FL/FLORA/MooseX-MethodAttributes-0.24.tar.gz
+/authors/id/F/FL/FLORA/MooseX-Role-WithOverloading-0.09.tar.gz
+/authors/id/F/FL/FLORA/Sub-Name-0.05.tar.gz
+/authors/id/F/FL/FLORA/namespace-clean-0.20.tar.gz
+/authors/id/F/FR/FREW/Data-Dumper-Concise-2.020.tar.gz
+/authors/id/F/FR/FREW/SQL-Abstract-1.72.tar.gz
+/authors/id/G/GA/GAAS/Data-Dump-1.19.tar.gz
+/authors/id/G/GA/GAAS/Digest-HMAC-1.02.tar.gz
+/authors/id/G/GA/GAAS/Encode-Locale-1.01.tar.gz
+/authors/id/G/GA/GAAS/File-Listing-6.00.tar.gz
+/authors/id/G/GA/GAAS/HTML-Parser-3.68.tar.gz
+/authors/id/G/GA/GAAS/HTTP-Cookies-6.00.tar.gz
+/authors/id/G/GA/GAAS/HTTP-Daemon-6.00.tar.gz
+/authors/id/G/GA/GAAS/HTTP-Date-6.00.tar.gz
+/authors/id/G/GA/GAAS/HTTP-Message-6.01.tar.gz
+/authors/id/G/GA/GAAS/HTTP-Negotiate-6.00.tar.gz
+/authors/id/G/GA/GAAS/IO-String-1.08.tar.gz
+/authors/id/G/GA/GAAS/LWP-MediaTypes-6.01.tar.gz
+/authors/id/G/GA/GAAS/Net-HTTP-6.00.tar.gz
+/authors/id/G/GA/GAAS/URI-1.58.tar.gz
+/authors/id/G/GA/GAAS/WWW-RobotRules-6.00.tar.gz
+/authors/id/G/GA/GAAS/libwww-perl-6.01.tar.gz
+/authors/id/G/GB/GBARR/CPAN-DistnameInfo-0.12.tar.gz
+/authors/id/G/GB/GBARR/Scalar-List-Utils-1.23.tar.gz
+/authors/id/G/GE/GETTY/HTTP-Body-1.11.tar.gz
+/authors/id/G/GR/GRODITI/MooseX-Types-Common-0.001002.tar.gz
+/authors/id/G/GR/GROMMEL/Math-Round-0.06.tar.gz
+/authors/id/I/IL/ILMARI/Class-Unload-0.07.tar.gz
+/authors/id/I/IN/INGY/Spiffy-0.30.tar.gz
+/authors/id/I/IN/INGY/Test-Base-0.59.tar.gz
+/authors/id/J/JE/JESSE/HTTP-Server-Simple-0.43.tar.gz
+/authors/id/J/JE/JESSE/HTTP-Server-Simple-0.44.tar.gz
+/authors/id/J/JH/JHOBLITT/DateTime-Format-ISO8601-0.07.tar.gz
+/authors/id/J/JP/JPEACOCK/version-0.88.tar.gz
+/authors/id/J/JR/JROCKWAY/Context-Preserve-0.01.tar.gz
+/authors/id/K/KA/KASEI/Class-Accessor-0.34.tar.gz
+/authors/id/K/KW/KWILLIAMS/Path-Class-0.23.tar.gz
+/authors/id/K/KW/KWILLIAMS/Probe-Perl-0.01.tar.gz
+/authors/id/L/LB/LBROCARD/Data-Page-2.02.tar.gz
+/authors/id/M/MA/MAKAMAKA/JSON-2.51.tar.gz
+/authors/id/M/MA/MAKAMAKA/JSON-PP-2.27105.tar.gz
+/authors/id/M/MA/MANU/Net-IP-1.25.tar.gz
+/authors/id/M/MA/MARKOV/MIME-Types-1.31.tar.gz
+/authors/id/M/MH/MHX/Devel-PPPort-3.19.tar.gz
+/authors/id/M/MI/MIROD/HTML-TreeBuilder-XPath-0.12.tar.gz
+/authors/id/M/MI/MIROD/XML-XPathEngine-0.12.tar.gz
+/authors/id/M/MI/MIYAGAWA/App-cpanminus-1.4004.tar.gz
+/authors/id/M/MI/MIYAGAWA/App-cpanminus-1.4007.tar.gz
+/authors/id/M/MI/MIYAGAWA/HTML-Selector-XPath-0.07.tar.gz
+/authors/id/M/MI/MIYAGAWA/Template-Plugin-Comma-0.04.tar.gz
+/authors/id/M/MI/MIYAGAWA/Web-Scraper-0.34.tar.gz
+/authors/id/M/ML/MLEHMANN/JSON-XS-2.3.tar.gz
+/authors/id/M/ML/MLEHMANN/common-sense-3.4.tar.gz
+/authors/id/M/MR/MRAMBERG/Text-SimpleTable-2.03.tar.gz
+/authors/id/M/MS/MSCHILLI/Log-Log4perl-1.32.tar.gz
+/authors/id/M/MS/MSCHWERN/Carp-Assert-0.20.tar.gz
+/authors/id/M/MS/MSCHWERN/ExtUtils-MakeMaker-6.56.tar.gz
+/authors/id/M/MS/MSCHWERN/Test-Simple-0.98.tar.gz
+/authors/id/M/MS/MSCHWERN/UNIVERSAL-require-0.13.tar.gz
+/authors/id/M/MS/MSTROUT/Catalyst-Plugin-Session-State-Cookie-0.17.tar.gz
+/authors/id/M/MS/MSTROUT/Catalyst-Plugin-Static-Simple-0.29.tar.gz
+/authors/id/N/NU/NUFFIN/Tie-ToObject-0.03.tar.gz
+/authors/id/O/OL/OLAF/Net-DNS-0.66.tar.gz
+/authors/id/O/OV/OVID/Sub-Override-0.08.tar.gz
+/authors/id/O/OV/OVID/aliased-0.30.tar.gz
+/authors/id/P/PE/PETDANCE/Carp-Assert-More-1.12.tar.gz
+/authors/id/P/PE/PETDANCE/Template-Timer-1.00.tar.gz
+/authors/id/P/PE/PETDANCE/Test-WWW-Mechanize-1.30.tar.gz
+/authors/id/P/PE/PETDANCE/WWW-Mechanize-1.66.tar.gz
+/authors/id/P/PM/PMQS/Compress-Raw-Bzip2-2.033.tar.gz
+/authors/id/P/PM/PMQS/Compress-Raw-Zlib-2.033.tar.gz
+/authors/id/P/PM/PMQS/IO-Compress-2.033.tar.gz
+/authors/id/R/RC/RCLAMP/Class-Accessor-Chained-0.01.tar.gz
+/authors/id/R/RC/RCLAMP/Devel-Caller-2.05.tar.gz
+/authors/id/R/RC/RCLAMP/File-Find-Rule-0.32.tar.gz
+/authors/id/R/RC/RCLAMP/Number-Compare-0.01.tar.gz
+/authors/id/R/RC/RCLAMP/Text-Glob-0.09.tar.gz
+/authors/id/R/RD/RDF/Clone-0.31.tar.gz
+/authors/id/R/RG/RGARCIA/Sub-Identify-0.04.tar.gz
+/authors/id/R/RG/RGARCIA/Test-LongString-0.14.tar.gz
+/authors/id/R/RG/RGARCIA/Test-LongString-0.15.tar.gz
+/authors/id/R/RI/RIBASUSHI/Class-C3-Componentised-1.0008.tar.gz
+/authors/id/R/RJ/RJBS/Data-OptList-0.106.tar.gz
+/authors/id/R/RJ/RJBS/Email-Address-1.892.tar.gz
+/authors/id/R/RJ/RJBS/Email-Date-Format-1.002.tar.gz
+/authors/id/R/RJ/RJBS/Email-MIME-1.908.tar.gz
+/authors/id/R/RJ/RJBS/Email-MIME-ContentType-1.015.tar.gz
+/authors/id/R/RJ/RJBS/Email-MIME-Encodings-1.313.tar.gz
+/authors/id/R/RJ/RJBS/Email-MessageID-1.402.tar.gz
+/authors/id/R/RJ/RJBS/Email-Send-2.198.tar.gz
+/authors/id/R/RJ/RJBS/Email-Simple-2.100.tar.gz
+/authors/id/R/RJ/RJBS/Email-Valid-0.184.tar.gz
+/authors/id/R/RJ/RJBS/Getopt-Long-Descriptive-0.089.tar.gz
+/authors/id/R/RJ/RJBS/Return-Value-1.666001.tar.gz
+/authors/id/R/RJ/RJBS/String-RewritePrefix-0.006.tar.gz
+/authors/id/R/RJ/RJBS/Sub-Exporter-0.982.tar.gz
+/authors/id/R/RJ/RJBS/Sub-Install-0.925.tar.gz
+/authors/id/R/RJ/RJBS/Test-Deep-0.108.tar.gz
+/authors/id/R/RJ/RJBS/Test-Fatal-0.003.tar.gz
+/authors/id/R/RJ/RJBS/Version-Requirements-0.101020.tar.gz
+/authors/id/R/RK/RKITOVER/Catalyst-Model-DBIC-Schema-0.48.tar.gz
+/authors/id/R/RK/RKITOVER/Catalyst-Plugin-SmartURI-0.036.tar.gz
+/authors/id/R/RK/RKITOVER/Class-Accessor-Grouped-0.10002.tar.gz
+/authors/id/R/RK/RKITOVER/DBIx-Class-Schema-Loader-0.07010.tar.gz
+/authors/id/R/RK/RKITOVER/Lingua-EN-Inflect-Phrase-0.04.tar.gz
+/authors/id/R/RK/RKITOVER/MooseX-Traits-Pluggable-0.10.tar.gz
+/authors/id/R/RK/RKITOVER/URI-SmartURI-0.031.tar.gz
+/authors/id/R/RO/ROBIN/PadWalker-1.92.tar.gz
+/authors/id/R/RO/ROODE/Readonly-1.03.tar.gz
+/authors/id/R/RS/RSCHUPP/Module-ScanDeps-1.00.tar.gz
+/authors/id/S/SA/SALVA/Sort-Key-1.28.tar.gz
+/authors/id/S/SA/SARTAK/Class-Load-0.06.tar.gz
+/authors/id/S/SD/SDP/Lingua-Stem-Fr-0.02.tar.gz
+/authors/id/S/SI/SIMON/Lingua-EN-Inflect-Number-1.1.tar.gz
+/authors/id/S/SI/SIMONW/Module-Pluggable-3.9.tar.gz
+/authors/id/S/SM/SMUELLER/Class-XSAccessor-1.11.tar.gz
+/authors/id/S/SM/SMUELLER/PAR-Dist-0.47.tar.gz
+/authors/id/S/SM/SMUELLER/PathTools-3.33.tar.gz
+/authors/id/S/SN/SNOWHARE/Lingua-Stem-0.84.tar.gz
+/authors/id/S/ST/STBEY/Carp-Clan-6.04.tar.gz
+/authors/id/S/ST/STEVAN/Tree-Simple-1.18.tar.gz
+/authors/id/S/ST/STEVAN/Tree-Simple-VisitorFactory-0.10.tar.gz
+/authors/id/T/TI/TIMB/DBI-1.616.tar.gz
+/authors/id/T/TJ/TJENNESS/File-Temp-0.22.tar.gz
+/authors/id/T/TL/TLINDEN/Config-General-2.50.tar.gz
+/authors/id/T/TM/TMTM/Class-Data-Inheritable-0.08.tar.gz
+/authors/id/T/TO/TODDR/Locale-Maketext-1.19.tar.gz
+/authors/id/T/TO/TOKUHIROM/Test-Requires-0.06.tar.gz
+/authors/id/T/TU/TURNSTEP/DBD-Pg-2.17.2.tar.gz
+/authors/id/T/TU/TURNSTEP/DBD-Pg-2.18.1.tar.gz
+/authors/id/U/UL/ULPFR/Text-German-0.06.tar.gz
+/authors/id/V/VP/VPIT/Variable-Magic-0.46.tar.gz
+/authors/id/X/XE/XERN/Lingua-PT-Stemmer-0.01.tar.gz
+/authors/id/Y/YV/YVES/ExtUtils-Install-1.54.tar.gz
diff --git a/perl-external/lib/perl5/local/lib.pm b/perl-external/lib/perl5/local/lib.pm
new file mode 100644
index 000000000..87d186903
--- /dev/null
+++ b/perl-external/lib/perl5/local/lib.pm
@@ -0,0 +1,1030 @@
+use strict;
+use warnings;
+
+package local::lib;
+
+use 5.008001; # probably works with earlier versions but I'm not supporting them
+ # (patches would, of course, be welcome)
+
+use File::Spec ();
+use File::Path ();
+use Carp ();
+use Config;
+
+our $VERSION = '1.008004'; # 1.8.4
+
+our @KNOWN_FLAGS = qw(--self-contained --deactivate --deactivate-all);
+
+sub DEACTIVATE_ONE () { 1 }
+sub DEACTIVATE_ALL () { 2 }
+
+sub INTERPOLATE_ENV () { 1 }
+sub LITERAL_ENV () { 0 }
+
+sub import {
+ my ($class, @args) = @_;
+
+ # Remember what PERL5LIB was when we started
+ my $perl5lib = $ENV{PERL5LIB} || '';
+
+ my %arg_store;
+ for my $arg (@args) {
+ # check for lethal dash first to stop processing before causing problems
+ if ($arg =~ /−/) {
+ die <<'DEATH';
+WHOA THERE! It looks like you've got some fancy dashes in your commandline!
+These are *not* the traditional -- dashes that software recognizes. You
+probably got these by copy-pasting from the perldoc for this module as
+rendered by a UTF8-capable formatter. This most typically happens on an OS X
+terminal, but can happen elsewhere too. Please try again after replacing the
+dashes with normal minus signs.
+DEATH
+ }
+ elsif(grep { $arg eq $_ } @KNOWN_FLAGS) {
+ (my $flag = $arg) =~ s/--//;
+ $arg_store{$flag} = 1;
+ }
+ elsif($arg =~ /^--/) {
+ die "Unknown import argument: $arg";
+ }
+ else {
+ # assume that what's left is a path
+ $arg_store{path} = $arg;
+ }
+ }
+
+ if($arg_store{'self-contained'}) {
+ die "FATAL: The local::lib --self-contained flag has never worked reliably and the original author, Mark Stosberg, was unable or unwilling to maintain it. As such, this flag has been removed from the local::lib codebase in order to prevent misunderstandings and potentially broken builds. The local::lib authors recommend that you look at the lib::core::only module shipped with this distribution in order to create a more robust environment that is equivalent to what --self-contained provided (although quite possibly not what you originally thought it provided due to the poor quality of the documentation, for which we apologise).\n";
+ }
+
+ my $deactivating = 0;
+ if ($arg_store{deactivate}) {
+ $deactivating = DEACTIVATE_ONE;
+ }
+ if ($arg_store{'deactivate-all'}) {
+ $deactivating = DEACTIVATE_ALL;
+ }
+
+ $arg_store{path} = $class->resolve_path($arg_store{path});
+ $class->setup_local_lib_for($arg_store{path}, $deactivating);
+
+ for (@INC) { # Untaint @INC
+ next if ref; # Skip entry if it is an ARRAY, CODE, blessed, etc.
+ m/(.*)/ and $_ = $1;
+ }
+}
+
+sub pipeline;
+
+sub pipeline {
+ my @methods = @_;
+ my $last = pop(@methods);
+ if (@methods) {
+ \sub {
+ my ($obj, @args) = @_;
+ $obj->${pipeline @methods}(
+ $obj->$last(@args)
+ );
+ };
+ } else {
+ \sub {
+ shift->$last(@_);
+ };
+ }
+}
+
+=begin testing
+
+#:: test pipeline
+
+package local::lib;
+
+{ package Foo; sub foo { -$_[1] } sub bar { $_[1]+2 } sub baz { $_[1]+3 } }
+my $foo = bless({}, 'Foo');
+Test::More::ok($foo->${pipeline qw(foo bar baz)}(10) == -15);
+
+=end testing
+
+=cut
+
+sub _uniq {
+ my %seen;
+ grep { ! $seen{$_}++ } @_;
+}
+
+sub resolve_path {
+ my ($class, $path) = @_;
+ $class->${pipeline qw(
+ resolve_relative_path
+ resolve_home_path
+ resolve_empty_path
+ )}($path);
+}
+
+sub resolve_empty_path {
+ my ($class, $path) = @_;
+ if (defined $path) {
+ $path;
+ } else {
+ '~/perl5';
+ }
+}
+
+=begin testing
+
+#:: test classmethod setup
+
+my $c = 'local::lib';
+
+=end testing
+
+=begin testing
+
+#:: test classmethod
+
+is($c->resolve_empty_path, '~/perl5');
+is($c->resolve_empty_path('foo'), 'foo');
+
+=end testing
+
+=cut
+
+sub resolve_home_path {
+ my ($class, $path) = @_;
+ return $path unless ($path =~ /^~/);
+ my ($user) = ($path =~ /^~([^\/]+)/); # can assume ^~ so undef for 'us'
+ my $tried_file_homedir;
+ my $homedir = do {
+ if (eval { require File::HomeDir } && $File::HomeDir::VERSION >= 0.65) {
+ $tried_file_homedir = 1;
+ if (defined $user) {
+ File::HomeDir->users_home($user);
+ } else {
+ File::HomeDir->my_home;
+ }
+ } else {
+ if (defined $user) {
+ (getpwnam $user)[7];
+ } else {
+ if (defined $ENV{HOME}) {
+ $ENV{HOME};
+ } else {
+ (getpwuid $<)[7];
+ }
+ }
+ }
+ };
+ unless (defined $homedir) {
+ Carp::croak(
+ "Couldn't resolve homedir for "
+ .(defined $user ? $user : 'current user')
+ .($tried_file_homedir ? '' : ' - consider installing File::HomeDir')
+ );
+ }
+ $path =~ s/^~[^\/]*/$homedir/;
+ $path;
+}
+
+sub resolve_relative_path {
+ my ($class, $path) = @_;
+ $path = File::Spec->rel2abs($path);
+}
+
+=begin testing
+
+#:: test classmethod
+
+local *File::Spec::rel2abs = sub { shift; 'FOO'.shift; };
+is($c->resolve_relative_path('bar'),'FOObar');
+
+=end testing
+
+=cut
+
+sub setup_local_lib_for {
+ my ($class, $path, $deactivating) = @_;
+
+ my $interpolate = LITERAL_ENV;
+ my @active_lls = $class->active_paths;
+
+ $path = $class->ensure_dir_structure_for($path);
+
+ if (! $deactivating) {
+ if (@active_lls && $active_lls[-1] eq $path) {
+ exit 0 if $0 eq '-';
+ return; # Asked to add what's already at the top of the stack
+ } elsif (grep { $_ eq $path} @active_lls) {
+ # Asked to add a dir that's lower in the stack -- so we remove it from
+ # where it is, and then add it back at the top.
+ $class->setup_env_hash_for($path, DEACTIVATE_ONE);
+ # Which means we can no longer output "PERL5LIB=...:$PERL5LIB" stuff
+ # anymore because we're taking something *out*.
+ $interpolate = INTERPOLATE_ENV;
+ }
+ }
+
+ if ($0 eq '-') {
+ $class->print_environment_vars_for($path, $deactivating, $interpolate);
+ exit 0;
+ } else {
+ $class->setup_env_hash_for($path, $deactivating);
+ @INC = _uniq(split($Config{path_sep}, $ENV{PERL5LIB}), @INC);
+ }
+}
+
+sub install_base_bin_path {
+ my ($class, $path) = @_;
+ File::Spec->catdir($path, 'bin');
+}
+
+sub install_base_perl_path {
+ my ($class, $path) = @_;
+ File::Spec->catdir($path, 'lib', 'perl5');
+}
+
+sub install_base_arch_path {
+ my ($class, $path) = @_;
+ File::Spec->catdir($class->install_base_perl_path($path), $Config{archname});
+}
+
+sub ensure_dir_structure_for {
+ my ($class, $path) = @_;
+ unless (-d $path) {
+ warn "Attempting to create directory ${path}\n";
+ }
+ File::Path::mkpath($path);
+ # Need to have the path exist to make a short name for it, so
+ # converting to a short name here.
+ $path = Win32::GetShortPathName($path) if $^O eq 'MSWin32';
+
+ return $path;
+}
+
+sub guess_shelltype {
+ my $shellbin = 'sh';
+ if(defined $ENV{'SHELL'}) {
+ my @shell_bin_path_parts = File::Spec->splitpath($ENV{'SHELL'});
+ $shellbin = $shell_bin_path_parts[-1];
+ }
+ my $shelltype = do {
+ local $_ = $shellbin;
+ if(/csh/) {
+ 'csh'
+ } else {
+ 'bourne'
+ }
+ };
+
+ # Both Win32 and Cygwin have $ENV{COMSPEC} set.
+ if (defined $ENV{'COMSPEC'} && $^O ne 'cygwin') {
+ my @shell_bin_path_parts = File::Spec->splitpath($ENV{'COMSPEC'});
+ $shellbin = $shell_bin_path_parts[-1];
+ $shelltype = do {
+ local $_ = $shellbin;
+ if(/command\.com/) {
+ 'win32'
+ } elsif(/cmd\.exe/) {
+ 'win32'
+ } elsif(/4nt\.exe/) {
+ 'win32'
+ } else {
+ $shelltype
+ }
+ };
+ }
+ return $shelltype;
+}
+
+sub print_environment_vars_for {
+ my ($class, $path, $deactivating, $interpolate) = @_;
+ print $class->environment_vars_string_for($path, $deactivating, $interpolate);
+}
+
+sub environment_vars_string_for {
+ my ($class, $path, $deactivating, $interpolate) = @_;
+ my @envs = $class->build_environment_vars_for($path, $deactivating, $interpolate);
+ my $out = '';
+
+ # rather basic csh detection, goes on the assumption that something won't
+ # call itself csh unless it really is. also, default to bourne in the
+ # pathological situation where a user doesn't have $ENV{SHELL} defined.
+ # note also that shells with funny names, like zoid, are assumed to be
+ # bourne.
+
+ my $shelltype = $class->guess_shelltype;
+
+ while (@envs) {
+ my ($name, $value) = (shift(@envs), shift(@envs));
+ $value =~ s/(\\")/\\$1/g if defined $value;
+ $out .= $class->${\"build_${shelltype}_env_declaration"}($name, $value);
+ }
+ return $out;
+}
+
+# simple routines that take two arguments: an %ENV key and a value. return
+# strings that are suitable for passing directly to the relevant shell to set
+# said key to said value.
+sub build_bourne_env_declaration {
+ my $class = shift;
+ my($name, $value) = @_;
+ return defined($value) ? qq{export ${name}="${value}";\n} : qq{unset ${name};\n};
+}
+
+sub build_csh_env_declaration {
+ my $class = shift;
+ my($name, $value) = @_;
+ return defined($value) ? qq{setenv ${name} "${value}"\n} : qq{unsetenv ${name}\n};
+}
+
+sub build_win32_env_declaration {
+ my $class = shift;
+ my($name, $value) = @_;
+ return defined($value) ? qq{set ${name}=${value}\n} : qq{set ${name}=\n};
+}
+
+sub setup_env_hash_for {
+ my ($class, $path, $deactivating) = @_;
+ my %envs = $class->build_environment_vars_for($path, $deactivating, INTERPOLATE_ENV);
+ @ENV{keys %envs} = values %envs;
+}
+
+sub build_environment_vars_for {
+ my ($class, $path, $deactivating, $interpolate) = @_;
+
+ if ($deactivating == DEACTIVATE_ONE) {
+ return $class->build_deactivate_environment_vars_for($path, $interpolate);
+ } elsif ($deactivating == DEACTIVATE_ALL) {
+ return $class->build_deact_all_environment_vars_for($path, $interpolate);
+ } else {
+ return $class->build_activate_environment_vars_for($path, $interpolate);
+ }
+}
+
+sub build_activate_environment_vars_for {
+ my ($class, $path, $interpolate) = @_;
+ return (
+ PERL_LOCAL_LIB_ROOT => join($Config{path_sep},
+ (($ENV{PERL_LOCAL_LIB_ROOT}||()) ?
+ ($interpolate == INTERPOLATE_ENV
+ ? ($ENV{PERL_LOCAL_LIB_ROOT}||())
+ : (($^O ne 'MSWin32') ? '$PERL_LOCAL_LIB_ROOT'
+ : '%PERL_LOCAL_LIB_ROOT%' ))
+ : ()),
+ $path
+ ),
+ PERL_MB_OPT => "--install_base ${path}",
+ PERL_MM_OPT => "INSTALL_BASE=${path}",
+ PERL5LIB => join($Config{path_sep},
+ $class->install_base_arch_path($path),
+ $class->install_base_perl_path($path),
+ (($ENV{PERL5LIB}||()) ?
+ ($interpolate == INTERPOLATE_ENV
+ ? ($ENV{PERL5LIB})
+ : (($^O ne 'MSWin32') ? '$PERL5LIB' : '%PERL5LIB%' ))
+ : ())
+ ),
+ PATH => join($Config{path_sep},
+ $class->install_base_bin_path($path),
+ ($interpolate == INTERPOLATE_ENV
+ ? ($ENV{PATH}||())
+ : (($^O ne 'MSWin32') ? '$PATH' : '%PATH%' ))
+ ),
+ )
+}
+
+sub active_paths {
+ my ($class) = @_;
+
+ return () unless defined $ENV{PERL_LOCAL_LIB_ROOT};
+ return split /\Q$Config{path_sep}/, $ENV{PERL_LOCAL_LIB_ROOT};
+}
+
+sub build_deactivate_environment_vars_for {
+ my ($class, $path, $interpolate) = @_;
+
+ my @active_lls = $class->active_paths;
+
+ if (!grep { $_ eq $path } @active_lls) {
+ warn "Tried to deactivate inactive local::lib '$path'\n";
+ return ();
+ }
+
+ my @new_ll_root = grep { $_ ne $path } @active_lls;
+ my @new_perl5lib = grep {
+ $_ ne $class->install_base_arch_path($path) &&
+ $_ ne $class->install_base_perl_path($path)
+ } split /\Q$Config{path_sep}/, $ENV{PERL5LIB};
+
+ my %env = (
+ PERL_LOCAL_LIB_ROOT => (@new_ll_root ?
+ join($Config{path_sep}, @new_ll_root) : undef
+ ),
+ PERL5LIB => (@new_perl5lib ?
+ join($Config{path_sep}, @new_perl5lib) : undef
+ ),
+ PATH => join($Config{path_sep},
+ grep { $_ ne $class->install_base_bin_path($path) }
+ split /\Q$Config{path_sep}/, $ENV{PATH}
+ ),
+ );
+
+ # If removing ourselves from the "top of the stack", set install paths to
+ # correspond with the new top of stack.
+ if ($active_lls[-1] eq $path) {
+ if (@active_lls > 1) {
+ my $new_top = $active_lls[-2];
+ %env = (%env,
+ PERL_MB_OPT => "--install_base ${new_top}",
+ PERL_MM_OPT => "INSTALL_BASE=${new_top}",
+ );
+ } else {
+ %env = (%env,
+ PERL_MB_OPT => undef,
+ PERL_MM_OPT => undef,
+ );
+ }
+ }
+
+ return %env;
+}
+
+sub build_deact_all_environment_vars_for {
+ my ($class, $path, $interpolate) = @_;
+
+ my @active_lls = $class->active_paths;
+
+ my @new_perl5lib = split /\Q$Config{path_sep}/, $ENV{PERL5LIB};
+ my @new_path = split /\Q$Config{path_sep}/, $ENV{PATH};
+
+ for my $path (@active_lls) {
+ @new_perl5lib = grep {
+ $_ ne $class->install_base_arch_path($path) &&
+ $_ ne $class->install_base_perl_path($path)
+ } @new_perl5lib;
+
+ @new_path = grep {
+ $_ ne $class->install_base_bin_path($path)
+ } @new_path;
+ }
+
+ my %env = (
+ PERL_LOCAL_LIB_ROOT => undef,
+ PERL_MM_OPT => undef,
+ PERL_MB_OPT => undef,
+ PERL5LIB => (@new_perl5lib ?
+ join($Config{path_sep}, @new_perl5lib) : undef
+ ),
+ PATH => join($Config{path_sep}, @new_path),
+ );
+
+ return %env;
+}
+
+=begin testing
+
+#:: test classmethod
+
+File::Path::rmtree('t/var/splat');
+
+$c->ensure_dir_structure_for('t/var/splat');
+
+ok(-d 't/var/splat');
+
+=end testing
+
+=encoding utf8
+
+=head1 NAME
+
+local::lib - create and use a local lib/ for perl modules with PERL5LIB
+
+=head1 SYNOPSIS
+
+In code -
+
+ use local::lib; # sets up a local lib at ~/perl5
+
+ use local::lib '~/foo'; # same, but ~/foo
+
+ # Or...
+ use FindBin;
+ use local::lib "$FindBin::Bin/../support"; # app-local support library
+
+From the shell -
+
+ # Install LWP and its missing dependencies to the '~/perl5' directory
+ perl -MCPAN -Mlocal::lib -e 'CPAN::install(LWP)'
+
+ # Just print out useful shell commands
+ $ perl -Mlocal::lib
+ export PERL_MB_OPT='--install_base /home/username/perl5'
+ export PERL_MM_OPT='INSTALL_BASE=/home/username/perl5'
+ export PERL5LIB='/home/username/perl5/lib/perl5/i386-linux:/home/username/perl5/lib/perl5'
+ export PATH="/home/username/perl5/bin:$PATH"
+
+=head2 The bootstrapping technique
+
+A typical way to install local::lib is using what is known as the
+"bootstrapping" technique. You would do this if your system administrator
+hasn't already installed local::lib. In this case, you'll need to install
+local::lib in your home directory.
+
+If you do have administrative privileges, you will still want to set up your
+environment variables, as discussed in step 4. Without this, you would still
+install the modules into the system CPAN installation and also your Perl scripts
+will not use the lib/ path you bootstrapped with local::lib.
+
+By default local::lib installs itself and the CPAN modules into ~/perl5.
+
+Windows users must also see L</Differences when using this module under Win32>.
+
+1. Download and unpack the local::lib tarball from CPAN (search for "Download"
+on the CPAN page about local::lib). Do this as an ordinary user, not as root
+or administrator. Unpack the file in your home directory or in any other
+convenient location.
+
+2. Run this:
+
+ perl Makefile.PL --bootstrap
+
+If the system asks you whether it should automatically configure as much
+as possible, you would typically answer yes.
+
+In order to install local::lib into a directory other than the default, you need
+to specify the name of the directory when you call bootstrap, as follows:
+
+ perl Makefile.PL --bootstrap=~/foo
+
+3. Run this: (local::lib assumes you have make installed on your system)
+
+ make test && make install
+
+4. Now we need to setup the appropriate environment variables, so that Perl
+starts using our newly generated lib/ directory. If you are using bash or
+any other Bourne shells, you can add this to your shell startup script this
+way:
+
+ echo 'eval $(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)' >>~/.bashrc
+
+If you are using C shell, you can do this as follows:
+
+ /bin/csh
+ echo $SHELL
+ /bin/csh
+ perl -I$HOME/perl5/lib/perl5 -Mlocal::lib >> ~/.cshrc
+
+If you passed to bootstrap a directory other than default, you also need to give that as
+import parameter to the call of the local::lib module like this way:
+
+ echo 'eval $(perl -I$HOME/foo/lib/perl5 -Mlocal::lib=$HOME/foo)' >>~/.bashrc
+
+After writing your shell configuration file, be sure to re-read it to get the
+changed settings into your current shell's environment. Bourne shells use
+C<. ~/.bashrc> for this, whereas C shells use C<source ~/.cshrc>.
+
+If you're on a slower machine, or are operating under draconian disk space
+limitations, you can disable the automatic generation of manpages from POD when
+installing modules by using the C<--no-manpages> argument when bootstrapping:
+
+ perl Makefile.PL --bootstrap --no-manpages
+
+To avoid doing several bootstrap for several Perl module environments on the
+same account, for example if you use it for several different deployed
+applications independently, you can use one bootstrapped local::lib
+installation to install modules in different directories directly this way:
+
+ cd ~/mydir1
+ perl -Mlocal::lib=./
+ eval $(perl -Mlocal::lib=./) ### To set the environment for this shell alone
+ printenv ### You will see that ~/mydir1 is in the PERL5LIB
+ perl -MCPAN -e install ... ### whatever modules you want
+ cd ../mydir2
+ ... REPEAT ...
+
+If you are working with several C<local::lib> environments, you may want to
+remove some of them from the current environment without disturbing the others.
+You can deactivate one environment like this (using bourne sh):
+
+ eval $(perl -Mlocal::lib=--deactivate,~/path)
+
+which will generate and run the commands needed to remove C<~/path> from your
+various search paths. Whichever environment was B<activated most recently> will
+remain the target for module installations. That is, if you activate
+C<~/path_A> and then you activate C<~/path_B>, new modules you install will go
+in C<~/path_B>. If you deactivate C<~/path_B> then modules will be installed
+into C<~/pathA> -- but if you deactivate C<~/path_A> then they will still be
+installed in C<~/pathB> because pathB was activated later.
+
+You can also ask C<local::lib> to clean itself completely out of the current
+shell's environment with the C<--deactivate-all> option.
+For multiple environments for multiple apps you may need to include a modified
+version of the C<< use FindBin >> instructions in the "In code" sample above.
+If you did something like the above, you have a set of Perl modules at C<<
+~/mydir1/lib >>. If you have a script at C<< ~/mydir1/scripts/myscript.pl >>,
+you need to tell it where to find the modules you installed for it at C<<
+~/mydir1/lib >>.
+
+In C<< ~/mydir1/scripts/myscript.pl >>:
+
+ use strict;
+ use warnings;
+ use local::lib "$FindBin::Bin/.."; ### points to ~/mydir1 and local::lib finds lib
+ use lib "$FindBin::Bin/../lib"; ### points to ~/mydir1/lib
+
+Put this before any BEGIN { ... } blocks that require the modules you installed.
+
+=head2 Differences when using this module under Win32
+
+To set up the proper environment variables for your current session of
+C<CMD.exe>, you can use this:
+
+ C:\>perl -Mlocal::lib
+ set PERL_MB_OPT=--install_base C:\DOCUME~1\ADMINI~1\perl5
+ set PERL_MM_OPT=INSTALL_BASE=C:\DOCUME~1\ADMINI~1\perl5
+ set PERL5LIB=C:\DOCUME~1\ADMINI~1\perl5\lib\perl5;C:\DOCUME~1\ADMINI~1\perl5\lib\perl5\MSWin32-x86-multi-thread
+ set PATH=C:\DOCUME~1\ADMINI~1\perl5\bin;%PATH%
+
+ ### To set the environment for this shell alone
+ C:\>perl -Mlocal::lib > %TEMP%\tmp.bat && %TEMP%\tmp.bat && del %TEMP%\temp.bat
+ ### instead of $(perl -Mlocal::lib=./)
+
+If you want the environment entries to persist, you'll need to add then to the
+Control Panel's System applet yourself or use L<App::local::lib::Win32Helper>.
+
+The "~" is translated to the user's profile directory (the directory named for
+the user under "Documents and Settings" (Windows XP or earlier) or "Users"
+(Windows Vista or later)) unless $ENV{HOME} exists. After that, the home
+directory is translated to a short name (which means the directory must exist)
+and the subdirectories are created.
+
+=head1 RATIONALE
+
+The version of a Perl package on your machine is not always the version you
+need. Obviously, the best thing to do would be to update to the version you
+need. However, you might be in a situation where you're prevented from doing
+this. Perhaps you don't have system administrator privileges; or perhaps you
+are using a package management system such as Debian, and nobody has yet gotten
+around to packaging up the version you need.
+
+local::lib solves this problem by allowing you to create your own directory of
+Perl packages downloaded from CPAN (in a multi-user system, this would typically
+be within your own home directory). The existing system Perl installation is
+not affected; you simply invoke Perl with special options so that Perl uses the
+packages in your own local package directory rather than the system packages.
+local::lib arranges things so that your locally installed version of the Perl
+packages takes precedence over the system installation.
+
+If you are using a package management system (such as Debian), you don't need to
+worry about Debian and CPAN stepping on each other's toes. Your local version
+of the packages will be written to an entirely separate directory from those
+installed by Debian.
+
+=head1 DESCRIPTION
+
+This module provides a quick, convenient way of bootstrapping a user-local Perl
+module library located within the user's home directory. It also constructs and
+prints out for the user the list of environment variables using the syntax
+appropriate for the user's current shell (as specified by the C<SHELL>
+environment variable), suitable for directly adding to one's shell
+configuration file.
+
+More generally, local::lib allows for the bootstrapping and usage of a
+directory containing Perl modules outside of Perl's C<@INC>. This makes it
+easier to ship an application with an app-specific copy of a Perl module, or
+collection of modules. Useful in cases like when an upstream maintainer hasn't
+applied a patch to a module of theirs that you need for your application.
+
+On import, local::lib sets the following environment variables to appropriate
+values:
+
+=over 4
+
+=item PERL_MB_OPT
+
+=item PERL_MM_OPT
+
+=item PERL5LIB
+
+=item PATH
+
+PATH is appended to, rather than clobbered.
+
+=back
+
+These values are then available for reference by any code after import.
+
+=head1 CREATING A SELF-CONTAINED SET OF MODULES
+
+See L<lib::core::only> for one way to do this - but note that
+there are a number of caveats, and the best approach is always to perform a
+build against a clean perl (i.e. site and vendor as close to empty as possible).
+
+=head1 OPTIONS
+
+Options are values that can be passed to the C<local::lib> import besides the
+directory to use. They are specified as C<use local::lib '--option'[, path];>
+or C<perl -Mlocal::lib=--option[,path]>.
+
+=head2 --deactivate
+
+Remove the chosen path (or the default path) from the module search paths if it
+was added by C<local::lib>, instead of adding it.
+
+=head2 --deactivate-all
+
+Remove all directories that were added to search paths by C<local::lib> from the
+search paths.
+
+=head1 METHODS
+
+=head2 ensure_dir_structure_for
+
+=over 4
+
+=item Arguments: $path
+
+=item Return value: None
+
+=back
+
+Attempts to create the given path, and all required parent directories. Throws
+an exception on failure.
+
+=head2 print_environment_vars_for
+
+=over 4
+
+=item Arguments: $path
+
+=item Return value: None
+
+=back
+
+Prints to standard output the variables listed above, properly set to use the
+given path as the base directory.
+
+=head2 build_environment_vars_for
+
+=over 4
+
+=item Arguments: $path, $interpolate
+
+=item Return value: \%environment_vars
+
+=back
+
+Returns a hash with the variables listed above, properly set to use the
+given path as the base directory.
+
+=head2 setup_env_hash_for
+
+=over 4
+
+=item Arguments: $path
+
+=item Return value: None
+
+=back
+
+Constructs the C<%ENV> keys for the given path, by calling
+L</build_environment_vars_for>.
+
+=head2 active_paths
+
+=over 4
+
+=item Arguments: None
+
+=item Return value: @paths
+
+=back
+
+Returns a list of active C<local::lib> paths, according to the
+C<PERL_LOCAL_LIB_ROOT> environment variable.
+
+=head2 install_base_perl_path
+
+=over 4
+
+=item Arguments: $path
+
+=item Return value: $install_base_perl_path
+
+=back
+
+Returns a path describing where to install the Perl modules for this local
+library installation. Appends the directories C<lib> and C<perl5> to the given
+path.
+
+=head2 install_base_arch_path
+
+=over 4
+
+=item Arguments: $path
+
+=item Return value: $install_base_arch_path
+
+=back
+
+Returns a path describing where to install the architecture-specific Perl
+modules for this local library installation. Based on the
+L</install_base_perl_path> method's return value, and appends the value of
+C<$Config{archname}>.
+
+=head2 install_base_bin_path
+
+=over 4
+
+=item Arguments: $path
+
+=item Return value: $install_base_bin_path
+
+=back
+
+Returns a path describing where to install the executable programs for this
+local library installation. Based on the L</install_base_perl_path> method's
+return value, and appends the directory C<bin>.
+
+=head2 resolve_empty_path
+
+=over 4
+
+=item Arguments: $path
+
+=item Return value: $base_path
+
+=back
+
+Builds and returns the base path into which to set up the local module
+installation. Defaults to C<~/perl5>.
+
+=head2 resolve_home_path
+
+=over 4
+
+=item Arguments: $path
+
+=item Return value: $home_path
+
+=back
+
+Attempts to find the user's home directory. If installed, uses C<File::HomeDir>
+for this purpose. If no definite answer is available, throws an exception.
+
+=head2 resolve_relative_path
+
+=over 4
+
+=item Arguments: $path
+
+=item Return value: $absolute_path
+
+=back
+
+Translates the given path into an absolute path.
+
+=head2 resolve_path
+
+=over 4
+
+=item Arguments: $path
+
+=item Return value: $absolute_path
+
+=back
+
+Calls the following in a pipeline, passing the result from the previous to the
+next, in an attempt to find where to configure the environment for a local
+library installation: L</resolve_empty_path>, L</resolve_home_path>,
+L</resolve_relative_path>. Passes the given path argument to
+L</resolve_empty_path> which then returns a result that is passed to
+L</resolve_home_path>, which then has its result passed to
+L</resolve_relative_path>. The result of this final call is returned from
+L</resolve_path>.
+
+=head1 A WARNING ABOUT UNINST=1
+
+Be careful about using local::lib in combination with "make install UNINST=1".
+The idea of this feature is that will uninstall an old version of a module
+before installing a new one. However it lacks a safety check that the old
+version and the new version will go in the same directory. Used in combination
+with local::lib, you can potentially delete a globally accessible version of a
+module while installing the new version in a local place. Only combine "make
+install UNINST=1" and local::lib if you understand these possible consequences.
+
+=head1 LIMITATIONS
+
+The perl toolchain is unable to handle directory names with spaces in it,
+so you cant put your local::lib bootstrap into a directory with spaces. What
+you can do is moving your local::lib to a directory with spaces B<after> you
+installed all modules inside your local::lib bootstrap. But be aware that you
+cant update or install CPAN modules after the move.
+
+Rather basic shell detection. Right now anything with csh in its name is
+assumed to be a C shell or something compatible, and everything else is assumed
+to be Bourne, except on Win32 systems. If the C<SHELL> environment variable is
+not set, a Bourne-compatible shell is assumed.
+
+Bootstrap is a hack and will use CPAN.pm for ExtUtils::MakeMaker even if you
+have CPANPLUS installed.
+
+Kills any existing PERL5LIB, PERL_MM_OPT or PERL_MB_OPT.
+
+Should probably auto-fixup CPAN config if not already done.
+
+Patches very much welcome for any of the above.
+
+On Win32 systems, does not have a way to write the created environment variables
+to the registry, so that they can persist through a reboot.
+
+=head1 TROUBLESHOOTING
+
+If you've configured local::lib to install CPAN modules somewhere in to your
+home directory, and at some point later you try to install a module with C<cpan
+-i Foo::Bar>, but it fails with an error like: C<Warning: You do not have
+permissions to install into /usr/lib64/perl5/site_perl/5.8.8/x86_64-linux at
+/usr/lib64/perl5/5.8.8/Foo/Bar.pm> and buried within the install log is an
+error saying C<'INSTALL_BASE' is not a known MakeMaker parameter name>, then
+you've somehow lost your updated ExtUtils::MakeMaker module.
+
+To remedy this situation, rerun the bootstrapping procedure documented above.
+
+Then, run C<rm -r ~/.cpan/build/Foo-Bar*>
+
+Finally, re-run C<cpan -i Foo::Bar> and it should install without problems.
+
+=head1 ENVIRONMENT
+
+=over 4
+
+=item SHELL
+
+=item COMSPEC
+
+local::lib looks at the user's C<SHELL> environment variable when printing out
+commands to add to the shell configuration file.
+
+On Win32 systems, C<COMSPEC> is also examined.
+
+=back
+
+=head1 SUPPORT
+
+IRC:
+
+ Join #local-lib on irc.perl.org.
+
+=head1 AUTHOR
+
+Matt S Trout <mst@shadowcat.co.uk> http://www.shadowcat.co.uk/
+
+auto_install fixes kindly sponsored by http://www.takkle.com/
+
+=head1 CONTRIBUTORS
+
+Patches to correctly output commands for csh style shells, as well as some
+documentation additions, contributed by Christopher Nehren <apeiron@cpan.org>.
+
+Doc patches for a custom local::lib directory, more cleanups in the english
+documentation and a L<german documentation|POD2::DE::local::lib> contributed by Torsten Raudssus
+<torsten@raudssus.de>.
+
+Hans Dieter Pearcey <hdp@cpan.org> sent in some additional tests for ensuring
+things will install properly, submitted a fix for the bug causing problems with
+writing Makefiles during bootstrapping, contributed an example program, and
+submitted yet another fix to ensure that local::lib can install and bootstrap
+properly. Many, many thanks!
+
+pattern of Freenode IRC contributed the beginnings of the Troubleshooting
+section. Many thanks!
+
+Patch to add Win32 support contributed by Curtis Jewell <csjewell@cpan.org>.
+
+Warnings for missing PATH/PERL5LIB (as when not running interactively) silenced
+by a patch from Marco Emilio Poleggi.
+
+Mark Stosberg <mark@summersault.com> provided the code for the now deleted
+'--self-contained' option.
+
+Documentation patches to make win32 usage clearer by
+David Mertens <dcmertens.perl@gmail.com> (run4flat).
+
+Brazilian L<portuguese translation|POD2::PT_BR::local::lib> and minor doc patches contributed by Breno
+G. de Oliveira <garu@cpan.org>.
+
+Improvements to stacking multiple local::lib dirs and removing them from the
+environment later on contributed by Andrew Rodland <arodland@cpan.org>.
+
+=head1 COPYRIGHT
+
+Copyright (c) 2007 - 2010 the local::lib L</AUTHOR> and L</CONTRIBUTORS> as
+listed above.
+
+=head1 LICENSE
+
+This library is free software and may be distributed under the same terms
+as perl itself.
+
+=cut
+
+1;
diff --git a/perl-external/minicpan/.gitignore b/perl-external/minicpan/.gitignore
new file mode 100644
index 000000000..40c08735d
--- /dev/null
+++ b/perl-external/minicpan/.gitignore
@@ -0,0 +1,3 @@
+authors
+modules/remote_packages.txt.gz
+indexer_reports
diff --git a/perl-external/minicpan/modules/02packages.details.txt.gz b/perl-external/minicpan/modules/02packages.details.txt.gz
new file mode 100644
index 000000000..1c902b5e8
--- /dev/null
+++ b/perl-external/minicpan/modules/02packages.details.txt.gz
Binary files differ
diff --git a/perl-external/minicpan/modules/03modlist.data.gz b/perl-external/minicpan/modules/03modlist.data.gz
new file mode 100644
index 000000000..9c8cc712e
--- /dev/null
+++ b/perl-external/minicpan/modules/03modlist.data.gz
Binary files differ
diff --git a/perl-external/modules.txt b/perl-external/modules.txt
new file mode 100644
index 000000000..ad4caa873
--- /dev/null
+++ b/perl-external/modules.txt
@@ -0,0 +1,56 @@
+App::cpanminus
+Bundle::DBD::Pg
+CPAN::ParseDistribution
+Catalyst
+Catalyst::Action::RenderView
+Catalyst::Authentication::Store::DBIx::Class
+Catalyst::Devel
+Catalyst::Model::Adaptor
+Catalyst::Plugin::Authentication
+Catalyst::Plugin::ConfigLoader
+Catalyst::Plugin::Session::Store::DBIC
+Catalyst::Plugin::SmartURI
+Catalyst::Plugin::Static::Simple
+Catalyst::Plugin::Unicode
+Catalyst::Plugin::Unicode::Encoding
+Catalyst::Runtime
+Catalyst::View::TT
+Class::Accessor
+Class::Unload
+Config::General
+DBIx::Class
+DBIx::Class::FilterColumn
+DBIx::Class::Schema::Loader
+DBIx::Class::Storage::DBI
+DateTime::Format::HTTP
+DateTime::Format::ISO8601
+DateTime::Format::Pg
+Email::Address
+Email::MIME
+Email::Send
+Email::Simple
+Email::Valid
+File::Path
+HTML::Entities
+HTTP::Server::Simple
+HTTP::Server::Simple::CGI
+IO::String
+JSON
+Locale::Maketext::Lexicon
+Moose
+Net::Domain::TLD
+Path::Class
+Readonly
+Return::Value
+Sort::Key
+Spiffy
+Sub::Override
+Template::Plugin::Comma
+Template::Plugin::DateTime::Format
+Term::Size::Any
+Test::More
+Test::WWW::Mechanize
+Test::WWW::Mechanize::Catalyst
+URI
+Web::Scraper
+namespace::autoclean
diff --git a/perllib/Carp/Always.pm b/perllib/Carp/Always.pm
deleted file mode 100644
index 68bcaee52..000000000
--- a/perllib/Carp/Always.pm
+++ /dev/null
@@ -1,162 +0,0 @@
-
-package Carp::Always;
-
-use 5.006;
-use strict;
-use warnings;
-
-our $VERSION = '0.09';
-
-use Carp qw(verbose); # makes carp() cluck and croak() confess
-
-sub _warn {
- if ($_[-1] =~ /\n$/s) {
- my $arg = pop @_;
- $arg =~ s/ at .*? line .*?\n$//s;
- push @_, $arg;
- }
- warn &Carp::longmess;
-}
-
-sub _die {
- if ($_[-1] =~ /\n$/s) {
- my $arg = pop @_;
- $arg =~ s/ at .*? line .*?\n$//s;
- push @_, $arg;
- }
- die &Carp::longmess;
-}
-
-my %OLD_SIG;
-
-BEGIN {
- @OLD_SIG{qw(__DIE__ __WARN__)} = @SIG{qw(__DIE__ __WARN__)};
- $SIG{__DIE__} = \&_die;
- $SIG{__WARN__} = \&_warn;
-}
-
-END {
- @SIG{qw(__DIE__ __WARN__)} = @OLD_SIG{qw(__DIE__ __WARN__)};
-}
-
-1;
-__END__
-
-=head1 NAME
-
-Carp::Always - Warns and dies noisily with stack backtraces
-
-=head1 SYNOPSIS
-
- use Carp::Always;
-
-makes every C<warn()> and C<die()> complains loudly in the calling package
-and elsewhere. More often used on the command line:
-
- perl -MCarp::Always script.pl
-
-=head1 DESCRIPTION
-
-This module is meant as a debugging aid. It can be
-used to make a script complain loudly with stack backtraces
-when warn()ing or die()ing.
-
-Here are how stack backtraces produced by this module
-looks:
-
- # it works for explicit die's and warn's
- $ perl -MCarp::Always -e 'sub f { die "arghh" }; sub g { f }; g'
- arghh at -e line 1
- main::f() called at -e line 1
- main::g() called at -e line 1
-
- # it works for interpreter-thrown failures
- $ perl -MCarp::Always -w -e 'sub f { $a = shift; @a = @$a };' \
- -e 'sub g { f(undef) }; g'
- Use of uninitialized value in array dereference at -e line 1
- main::f('undef') called at -e line 2
- main::g() called at -e line 2
-
-In the implementation, the C<Carp> module does
-the heavy work, through C<longmess()>. The
-actual implementation sets the signal hooks
-C<$SIG{__WARN__}> and C<$SIG{__DIE__}> to
-emit the stack backtraces.
-
-Oh, by the way, C<carp> and C<croak> when requiring/using
-the C<Carp> module are also made verbose, behaving
-like C<cloak> and C<confess>, respectively.
-
-=head2 EXPORT
-
-Nothing at all is exported.
-
-=head1 ACKNOWLEDGMENTS
-
-This module was born as a reaction to a release
-of L<Acme::JavaTrace> by Sébastien Aperghis-Tramoni.
-Sébastien also has a newer module called
-L<Devel::SimpleTrace> with the same code and fewer flame
-comments on docs. The pruning of the uselessly long
-docs of this module were prodded by Michael Schwern.
-
-Schwern and others told me "the module name stinked" -
-it was called C<Carp::Indeed>. After thinking long
-and not getting nowhere, I went with nuffin's suggestion
-and now it is called C<Carp::Always>.
-C<Carp::Indeed> which is now deprecate
-lives in its own distribution (which won't go anywhere
-but will stay there as a redirection to this module).
-
-=head1 SEE ALSO
-
-=over 4
-
-=item *
-
-L<Carp>
-
-=item *
-
-L<Acme::JavaTrace> and L<Devel::SimpleTrace>
-
-=back
-
-Please report bugs via CPAN RT
-http://rt.cpan.org/NoAuth/Bugs.html?Dist=Carp-Always.
-
-=head1 BUGS
-
-Every (un)deserving module has its own pet bugs.
-
-=over 4
-
-=item *
-
-This module does not play well with other modules which fusses
-around with C<warn>, C<die>, C<$SIG{'__WARN__'}>,
-C<$SIG{'__DIE__'}>.
-
-=item *
-
-Test scripts are good. I should write more of these.
-
-=item *
-
-I don't know if this module name is still a bug as it was
-at the time of C<Carp::Indeed>.
-
-=back
-
-=head1 AUTHOR
-
-Adriano Ferreira, E<lt>ferreira@cpan.orgE<gt>
-
-=head1 COPYRIGHT AND LICENSE
-
-Copyright (C) 2005-2007 by Adriano R. Ferreira
-
-This library is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself.
-
-=cut
diff --git a/perllib/Catalyst/Plugin/Session/State/Cookie.pm b/perllib/Catalyst/Plugin/Session/State/Cookie.pm
new file mode 100644
index 000000000..7ffc77f1f
--- /dev/null
+++ b/perllib/Catalyst/Plugin/Session/State/Cookie.pm
@@ -0,0 +1,357 @@
+package Catalyst::Plugin::Session::State::Cookie;
+use Moose;
+use namespace::autoclean;
+
+extends 'Catalyst::Plugin::Session::State';
+
+use MRO::Compat;
+use Catalyst::Utils ();
+
+our $VERSION = "0.17";
+
+has _deleted_session_id => ( is => 'rw' );
+
+sub setup_session {
+ my $c = shift;
+
+ $c->maybe::next::method(@_);
+
+ $c->_session_plugin_config->{cookie_name}
+ ||= Catalyst::Utils::appprefix($c) . '_session';
+}
+
+sub extend_session_id {
+ my ( $c, $sid, $expires ) = @_;
+
+ if ( my $cookie = $c->get_session_cookie ) {
+ $c->update_session_cookie( $c->make_session_cookie( $sid ) );
+ }
+
+ $c->maybe::next::method( $sid, $expires );
+}
+
+sub set_session_id {
+ my ( $c, $sid ) = @_;
+
+ $c->update_session_cookie( $c->make_session_cookie( $sid ) );
+
+ return $c->maybe::next::method($sid);
+}
+
+sub update_session_cookie {
+ my ( $c, $updated ) = @_;
+
+ unless ( $c->cookie_is_rejecting( $updated ) ) {
+ my $cookie_name = $c->_session_plugin_config->{cookie_name};
+ $c->response->cookies->{$cookie_name} = $updated;
+ }
+}
+
+sub cookie_is_rejecting {
+ my ( $c, $cookie ) = @_;
+
+ if ( $cookie->{path} ) {
+ return 1 if index '/'.$c->request->path, $cookie->{path};
+ }
+
+ return 0;
+}
+
+sub make_session_cookie {
+ my ( $c, $sid, %attrs ) = @_;
+
+ my $cfg = $c->_session_plugin_config;
+ my $cookie = {
+ value => $sid,
+ ( $cfg->{cookie_domain} ? ( domain => $cfg->{cookie_domain} ) : () ),
+ ( $cfg->{cookie_path} ? ( path => $cfg->{cookie_path} ) : () ),
+ %attrs,
+ };
+
+ unless ( exists $cookie->{expires} ) {
+ $cookie->{expires} = $c->calculate_session_cookie_expires();
+ }
+
+ #beware: we have to accept also the old syntax "cookie_secure = true"
+ my $sec = $cfg->{cookie_secure} || 0; # default = 0 (not set)
+ $cookie->{secure} = 1 unless ( ($sec==0) || ($sec==2) );
+ $cookie->{secure} = 1 if ( ($sec==2) && $c->req->secure );
+
+ $cookie->{httponly} = $cfg->{cookie_httponly};
+ $cookie->{httponly} = 1
+ unless defined $cookie->{httponly}; # default = 1 (set httponly)
+
+ return $cookie;
+}
+
+sub calc_expiry { # compat
+ my $c = shift;
+ $c->maybe::next::method( @_ ) || $c->calculate_session_cookie_expires( @_ );
+}
+
+sub calculate_session_cookie_expires {
+ my $c = shift;
+ my $cfg = $c->_session_plugin_config;
+
+ my $value = $c->maybe::next::method(@_);
+ return $value if $value;
+
+ if ( exists $c->session->{__cookie_expires} ) {
+ if ( $c->session->{__cookie_expires} > 0 ) {
+ return time() + $c->session->{__cookie_expires};
+ }
+ else {
+ return undef;
+ }
+ }
+ elsif ( exists $cfg->{cookie_expires} ) {
+ if ( $cfg->{cookie_expires} > 0 ) {
+ return time() + $cfg->{cookie_expires};
+ }
+ else {
+ return undef;
+ }
+ }
+ else {
+ return $c->session_expires;
+ }
+}
+
+sub set_session_cookie_expire {
+ my $c = shift;
+ my $val = shift;
+
+ if ( defined $val ) {
+ $c->session->{__cookie_expires} = $val;
+ }
+ else {
+ delete $c->session->{__cookie_expires};
+ }
+ return 1;
+}
+
+sub get_session_cookie {
+ my $c = shift;
+
+ my $cookie_name = $c->_session_plugin_config->{cookie_name};
+
+ return $c->request->cookies->{$cookie_name};
+}
+
+sub get_session_id {
+ my $c = shift;
+
+ if ( !$c->_deleted_session_id and my $cookie = $c->get_session_cookie ) {
+ my $sid = $cookie->value;
+ $c->log->debug(qq/Found sessionid "$sid" in cookie/) if $c->debug;
+ return $sid if $sid;
+ }
+
+ $c->maybe::next::method(@_);
+}
+
+sub delete_session_id {
+ my ( $c, $sid ) = @_;
+
+ $c->_deleted_session_id(1); # to prevent get_session_id from returning it
+
+ $c->update_session_cookie( $c->make_session_cookie( $sid, expires => 0 ) );
+
+ $c->maybe::next::method($sid);
+}
+
+__PACKAGE__
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Catalyst::Plugin::Session::State::Cookie - Maintain session IDs using cookies.
+
+=head1 SYNOPSIS
+
+ use Catalyst qw/Session Session::State::Cookie Session::Store::Foo/;
+
+=head1 DESCRIPTION
+
+In order for L<Catalyst::Plugin::Session> to work the session ID needs to be
+stored on the client, and the session data needs to be stored on the server.
+
+This plugin stores the session ID on the client using the cookie mechanism.
+
+=head1 PUBLIC METHODS
+
+=head2 set_session_cookie_expire $ttl_in_seconds
+
+ $c->set_session_cookie_expire(3600); # set to 1 hour
+ $c->set_session_cookie_expire(0); # expire with browser session
+ $c->set_session_cookie_expire(undef); # fallback to default
+
+This lets you change the expiry for the current session's cookie. You can set a
+number of seconds, 0 to expire the cookie when the browser quits or undef to
+fallback to the configured defaults. The value you choose is persisted.
+
+Note this value has no effect on the exipry in the session store - it only
+affects the cookie itself.
+
+=head1 METHODS
+
+=over 4
+
+=item make_session_cookie
+
+Returns a hash reference with the default values for new cookies.
+
+=item update_session_cookie $hash_ref
+
+Sets the cookie based on C<cookie_name> in the response object.
+
+=item calc_expiry
+
+=item calculate_session_cookie_expires
+
+=item cookie_is_rejecting
+
+=item delete_session_id
+
+=item extend_session_id
+
+=item get_session_cookie
+
+=item get_session_id
+
+=item set_session_id
+
+=back
+
+=head1 EXTENDED METHODS
+
+=over 4
+
+=item prepare_cookies
+
+Will restore if an appropriate cookie is found.
+
+=item finalize_cookies
+
+Will set a cookie called C<session> if it doesn't exist or if its value is not
+the current session id.
+
+=item setup_session
+
+Will set the C<cookie_name> parameter to its default value if it isn't set.
+
+=back
+
+=head1 CONFIGURATION
+
+=over 4
+
+=item cookie_name
+
+The name of the cookie to store (defaults to C<Catalyst::Utils::apprefix($c) . '_session'>).
+
+=item cookie_domain
+
+The name of the domain to store in the cookie (defaults to current host)
+
+=item cookie_expires
+
+Number of seconds from now you want to elapse before cookie will expire.
+Set to 0 to create a session cookie, ie one which will die when the
+user's browser is shut down.
+
+=item cookie_secure
+
+If this attribute B<set to 0> the cookie will not have the secure flag.
+
+If this attribute B<set to 1> (or true for backward compatibility) - the cookie
+sent by the server to the client will get the secure flag that tells the browser
+to send this cookie back to the server only via HTTPS.
+
+If this attribute B<set to 2> then the cookie will get the secure flag only if
+the request that caused cookie generation was sent over https (this option is
+not good if you are mixing https and http in your application).
+
+Default value is 0.
+
+=item cookie_httponly
+
+If this attribute B<set to 0>, the cookie will not have HTTPOnly flag.
+
+If this attribute B<set to 1>, the cookie will got HTTPOnly flag that should
+prevent client side Javascript accessing the cookie value - this makes some
+sort of session hijacking attacks significantly harder. Unfortunately not all
+browsers support this flag (MSIE 6 SP1+, Firefox 3.0.0.6+, Opera 9.5+); if
+a browser is not aware of HTTPOnly the flag will be ignored.
+
+Default value is 1.
+
+Note1: Many peole are confused by the name "HTTPOnly" - it B<does not mean>
+that this cookie works only over HTTP and not over HTTPS.
+
+Note2: This parameter requires Catalyst::Runtime 5.80005 otherwise is skipped.
+
+=item cookie_path
+
+The path of the request url where cookie should be baked.
+
+=back
+
+For example, you could stick this in MyApp.pm:
+
+ __PACKAGE__->config( 'Plugin::Session' => {
+ cookie_domain => '.mydomain.com',
+ });
+
+=head1 CAVEATS
+
+Sessions have to be created before the first write to be saved. For example:
+
+ sub action : Local {
+ my ( $self, $c ) = @_;
+ $c->res->write("foo");
+ $c->session( ... );
+ ...
+ }
+
+Will cause a session ID to not be set, because by the time a session is
+actually created the headers have already been sent to the client.
+
+=head1 SEE ALSO
+
+L<Catalyst>, L<Catalyst::Plugin::Session>.
+
+=head1 AUTHORS
+
+Yuval Kogman E<lt>nothingmuch@woobling.orgE<gt>
+
+=head1 CONTRIBUTORS
+
+This module is derived from L<Catalyst::Plugin::Session::FastMmap> code, and
+has been heavily modified since.
+
+ Andrew Ford
+ Andy Grundman
+ Christian Hansen
+ Marcus Ramberg
+ Jonathan Rockway E<lt>jrockway@cpan.orgE<gt>
+ Sebastian Riedel
+ Florian Ragwitz
+
+=head1 COPYRIGHT
+
+Copyright (c) 2005 - 2009
+the Catalyst::Plugin::Session::State::Cookie L</AUTHORS> and L</CONTRIBUTORS>
+as listed above.
+
+=head1 LICENSE
+
+This program is free software, you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=cut
+
+1;
diff --git a/perllib/Cobrand.pm b/perllib/Cobrand.pm
deleted file mode 100644
index b1ec6ba75..000000000
--- a/perllib/Cobrand.pm
+++ /dev/null
@@ -1,249 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Cobrand.pm:
-# Cobranding for FixMyStreet.
-#
-#
-# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
-# Email: louise@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: Cobrand.pm,v 1.58 2010-01-06 12:33:25 louise Exp $
-
-package Cobrand;
-use strict;
-use Carp;
-
-=item get_allowed_cobrands
-
-Return an array reference of allowed cobrand subdomains
-
-=cut
-sub get_allowed_cobrands {
- my $allowed_cobrand_string = mySociety::Config::get('ALLOWED_COBRANDS');
- my @allowed_cobrands = split(/\|/, $allowed_cobrand_string);
- return \@allowed_cobrands;
-}
-
-# Cobrand calling functions
-my %fns = (
- # Return a site restriction clause and a site key if the cobrand uses a subset of the FixMyStreet
- # data. Parameter is any extra data the cobrand needs. Returns an empty string and site key 0
- # if the cobrand uses all the data.
- 'site_restriction' => { default => '["", 0]' },
- # Return a contact restriction clause if the cobrand uses a subset of the FixMyStreet contact data.
- 'contact_restriction' => { default => "''" },
- # Return the base url to use in links in emails for the cobranded version of the site, parameter is extra data.
- 'base_url_for_emails' => { default => 'base_url($cobrand)' },
- # Base URL for the admin interface.
- 'admin_base_url' => { default => '0' },
- # URL for writetothem; parameter is COBRAND_DATA.
- 'writetothem_url' => { default => '0' },
- # Return the base url for the cobranded version of the site
- 'base_url' => { default => "mySociety::Config::get('BASE_URL')" },
- # Return the text that prompts the user to enter their postcode/place name. Parameter is QUERY
- 'enter_postcode_text' => { default => '""' },
- # Set the language and domain of the site based on the cobrand and host
- 'set_lang_and_domain' => { default => '\&default_set_lang_and_domain' },
- # Return HTML for a list of alert options for the cobrand, given QUERY and OPTIONS.
- 'alert_list_options' => { default => '0' },
- # Return N recent photos. If EASTING, NORTHING and DISTANCE are supplied, the photos must be attached to problems
- # within DISTANCE of the point defined by EASTING and NORTHING.
- 'recent_photos' => { default => '\&Problems::recent_photos' },
- # Return recent problems on the site.
- 'recent' => { default => '\&Problems::recent' },
- # Given a QUERY, return a block of html for showing front stats for the site
- 'front_stats' => { default => '\&Problems::front_stats' },
- # Given a STRING ($_[1]) representing a location and a QUERY, return a string that
- # includes any disambiguating information available
- 'disambiguate_location' => { default => '"$_[1]&gl=uk"' },
- # Parameter is EPOCHTIME
- 'prettify_epoch' => { default => '0' },
- # Parameters are FORM_NAME, QUERY. Return HTML for any extra needed elements for FORM_NAME
- 'form_elements' => { default => "''" },
- # Parameter is UPDATE_DATA, a reference to a hash of non-cobranded update data. Return cobrand extra data for the update
- 'cobrand_data_for_generic_update' => { default => "''" },
- # Parameter is PROBLEM_DATA, a reference to a hash of non-cobranded problem data. Return cobrand extra data for the problem
- 'cobrand_data_for_generic_problem' => { default => "''" },
- # Parameter is QUERY. Return a string of extra data to be stored with a problem
- 'extra_problem_data' => { default => "''" },
- # Parameter is QUERY. Return a string of extra data to be stored with an update
- 'extra_update_data' => { default => "''" },
- # Parameter is QUERY. Return a string of extra data to be stored with an alert
- 'extra_alert_data' => { default => "''" },
- # Given a QUERY, extract any extra data required by the cobrand
- 'extra_data' => { default => "''" },
- # Given a QUERY, return a hash of extra params to be included in
- # any URLs in links produced on the page returned by that query.
- 'extra_params' => { default => "''" },
- # Returns any extra text to be displayed with a PROBLEM.
- 'extra_problem_meta_text' => { default => "''" },
- # Returns any extra text to be displayed with an UPDATE.
- 'extra_update_meta_text' => { default => "''" },
- # Given a URL ($_[1]), QUERY, EXTRA_DATA, return a URL with any extra params needed appended to it.
- 'url' => { default => '$_[1]' },
- # Return any params to be added to responses
- 'header_params' => { default => '{}' },
- # Parameter is QUERY. Return some js to set the root path from which AJAX
- # queries should be made.
- 'root_path_js' => { default => "'var root_path = \"\";'" },
- # Return the title to be used in page heads.
- 'site_title' => { default => "''" },
- # Return the maximum number of items to be given in the list of reports on the map
- 'on_map_list_limit' => { default => 'undef' },
- # Return a boolean indicating whether the cobrand allows photo uploads
- 'allow_photo_upload' => { default => '1' },
- # Return a boolean indicating whether the cobrand allows the display of crosssell adverts
- 'allow_crosssell_adverts' => { default => '1' },
- # Return a boolean indicating whether the cobrand allows photo display
- 'allow_photo_display' => { default => '1' },
- # Return a boolean indication whether users should see links next to updates allowing them
- # to report them as offensive.
- 'allow_update_reporting' => { default => '0' },
- # Parameters are LOCATION, QUERY. Return a boolean indicating whether the
- # string LOCATION passes the cobrands checks.
- 'geocoded_string_check' => { default => '1' },
- # Paramters are COUNCILS, QUERY, CONTEXT. Return a boolean indicating whether
- # COUNCILS pass any extra checks. CONTEXT is where we are on the site.
- 'council_check' => { default => "[1, '']" },
- # Return an XSL to be used in rendering feeds
- 'feed_xsl' => { default => "'/xsl.xsl'" },
- # Return a boolean indicating whether the cobrand displays a report of all councils
- 'all_councils_report' => { default => '1' },
- # Return a boolean indicating whether people should be asked whether this
- # is the first time they've reported a problem.
- 'ask_ever_reported' => { default => '1' },
- # List of names of pages to display on the admin interface
- 'admin_pages' => { default => '0' },
- # Show the problem creation graph in the admin interface
- 'admin_show_creation_graph' => { default => '1' },
- # The MaPit types this site handles
- 'area_types' => { default => '[qw(DIS LBO MTD UTA CTY COI)]' },
- 'area_min_generation' => { default => '10' },
- # Some cobrands that use a Tilma map have a smaller mid-point to make pin centred
- 'tilma_mid_point' => { default => '""' },
-);
-
-foreach (keys %fns) {
- die "Default must be specified for $_" unless $fns{$_}{default} ne '';
- eval <<EOF;
-sub $_ {
- my (\$cobrand, \@args) = \@_;
- return call(\$cobrand, '$_', $fns{$_}{default}, \@args);
-}
-EOF
-}
-
-# This is the main Cobrand calling function that sees if the Cobrand handles
-# the function and responds appropriately.
-sub call {
- my ($cobrand, $fn, $default, @args) = @_;
- return call_default($default, @args) unless $cobrand;
- my $handle = cobrand_handle($cobrand);
- return call_default($default, @args) unless $handle && $handle->can($fn);
- return $handle->$fn(@args);
-}
-
-# If we're not in a Cobrand, or the Cobrand module doesn't offer a function,
-# this function works out how to return the default response
-sub call_default {
- my ($default, @args) = @_;
- return $default unless ref $default;
- return @$default if ref $default eq 'ARRAY'; # Arrays passed back as values
- return $default if ref $default eq 'HASH'; # Hashes passed back as reference
- return $default->(@args) if ref $default eq 'CODE'; # Functions are called.
- die "Default of $default treatment unknown";
-}
-
-=item cobrand_handle Q
-
-Given a query that has the name of a site set, return a handle to the Util module for that
-site, if one exists, or zero if not.
-
-=cut
-sub cobrand_handle {
- my $cobrand = shift;
-
- our %handles;
-
- # Once we have a handle defined, return it.
- return $handles{$cobrand} if defined $handles{$cobrand};
-
- my $cobrand_class = ucfirst($cobrand);
- my $class = "Cobrands::" . $cobrand_class . "::Util";
- eval "use $class";
-
- eval{ $handles{$cobrand} = $class->new };
- $handles{$cobrand} = 0 if $@;
- return $handles{$cobrand};
-}
-
-# Cobrand functions to fetch config variables
-%fns = (
- # Return the contact name for the cobranded version of the site
- # (to be used in emails).
- 'contact_name' => 'CONTACT_NAME',
- # Return the contact email for the cobranded version of the site
- 'contact_email' => 'CONTACT_EMAIL',
-);
-
-foreach (keys %fns) {
- eval <<EOF;
-sub $_ {
- my \$cobrand = shift;
- return get_cobrand_conf(\$cobrand, '$fns{$_}');
-}
-EOF
-}
-
-=item get_cobrand_conf COBRAND KEY
-
-Get the value for KEY from the config file for COBRAND
-
-=cut
-sub get_cobrand_conf {
- my ($cobrand, $key) = @_;
- my $value;
- if ($cobrand){
- (my $dir = __FILE__) =~ s{/[^/]*?$}{};
- if (-e "$dir/../conf/cobrands/$cobrand/general"){
- mySociety::Config::set_file("$dir/../conf/cobrands/$cobrand/general");
- $cobrand = uc($cobrand);
- $value = mySociety::Config::get($key . "_" . $cobrand, undef);
- mySociety::Config::set_file("$dir/../conf/general");
- }
- }
- if (!defined($value)){
- $value = mySociety::Config::get($key);
- }
- return $value;
-}
-
-=item email_host COBRAND
-
-Return the virtual host that sends email for this cobrand
-
-=cut
-
-sub email_host {
- my ($cobrand) = @_;
- my $email_vhost = mySociety::Config::get('EMAIL_VHOST');
- if ($cobrand) {
- $email_vhost = mySociety::Config::get('EMAIL_VHOST_'. uc($cobrand), $email_vhost);
- }
- if ($email_vhost && 'http://' . $email_vhost eq mySociety::Config::get('BASE_URL')) {
- return 1;
- } else {
- return 0;
- }
-}
-
-# Default things to do for the set_lang_and_domain call
-sub default_set_lang_and_domain {
- my ($lang, $unicode) = @_;
- mySociety::Locale::negotiate_language('en-gb,English,en_GB|nb,Norwegian,nb_NO', $lang); # XXX Testing
- mySociety::Locale::gettext_domain('FixMyStreet', $unicode);
- mySociety::Locale::change();
-}
-
-1;
-
diff --git a/perllib/Cobrands/.gitignore b/perllib/Cobrands/.gitignore
deleted file mode 100644
index 77650dd46..000000000
--- a/perllib/Cobrands/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/Cities \ No newline at end of file
diff --git a/perllib/Cobrands/Barnet/Util.pm b/perllib/Cobrands/Barnet/Util.pm
deleted file mode 100644
index e4115c232..000000000
--- a/perllib/Cobrands/Barnet/Util.pm
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Util.pm:
-# Barnet cobranding for FixMyStreet.
-#
-# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-
-package Cobrands::Barnet::Util;
-use strict;
-use Carp;
-use URI::Escape;
-use mySociety::VotingArea;
-
-sub new {
- my $class = shift;
- return bless {}, $class;
-}
-
-=item site_restriction Q
-
-Return a site restriction clause and a site key.
-
-=cut
-sub site_restriction{
- return ("and council='2489'", 'barnet');
-}
-
-=item
-
-Return the base url for this cobranded site
-
-=cut
-
-sub base_url {
- my $base_url = mySociety::Config::get('BASE_URL');
- if ($base_url !~ /barnet/) {
- $base_url =~ s/http:\/\/(?!www\.)/http:\/\/barnet\./g;
- $base_url =~ s/http:\/\/www\./http:\/\/barnet\./g;
- }
- return $base_url;
-}
-
-=item site_title
-
-Return the title to be used in page heads
-
-=cut
-
-sub site_title {
- my ($self) = @_;
- return 'Barnet Council FixMyStreet';
-}
-
-sub enter_postcode_text {
- my ($self,$q) = @_;
- return 'Enter a Barnet postcode, or street name and area';
-}
-
-=item council_check COUNCILS QUERY CONTEXT
-
-Return a boolean indicating whether COUNCILS are okay for the location
-in the QUERY, and an error message appropriate to the CONTEXT.
-
-=cut
-
-sub council_check {
- my ($self, $params, $q, $context) = @_;
- my $councils;
- if ($params->{all_councils}) {
- $councils = $params->{all_councils};
- } elsif (defined $params->{lat}) {
- my $parent_types = $mySociety::VotingArea::council_parent_types;
- $councils = mySociety::MaPit::call('point', "4326/$params->{lon},$params->{lat}", type => $parent_types);
- }
- my $council_match = defined $councils->{2489};
- if ($council_match) {
- return 1;
- }
- my $url = 'http://www.fixmystreet.com/';
- $url .= 'alert' if $context eq 'alert';
- $url .= '?pc=' . URI::Escape::uri_escape_utf8($q->param('pc')) if $q->param('pc');
- my $error_msg = "That location is not covered by Barnet.
-Please visit <a href=\"$url\">the main FixMyStreet site</a>.";
- return (0, $error_msg);
-}
-
-# All reports page only has the one council.
-sub all_councils_report {
- return 0;
-}
-
-=item disambiguate_location S Q
-
-Given a string representing a location (street and area expected),
-bias the viewport to around Barnet.
-
-=cut
-
-sub disambiguate_location {
- my ($self, $s, $q) = @_;
- $s = "ll=51.612832,-0.218169&spn=0.0563,0.09&$s";
- return $s;
-}
-
-sub recent_photos {
- my ($self, $num, $lat, $lon, $dist) = @_;
- $num = 2 if $num == 3;
- return Problems::recent_photos($num, $lat, $lon, $dist);
-}
-
-sub tilma_mid_point {
- return 189;
-}
-
-1;
-
diff --git a/perllib/Cobrands/Emptyhomes/Util.pm b/perllib/Cobrands/Emptyhomes/Util.pm
deleted file mode 100644
index d23857f50..000000000
--- a/perllib/Cobrands/Emptyhomes/Util.pm
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Util.pm:
-# Emptyhomes Cobranding for FixMyStreet.
-#
-# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
-# Email: louise@mysociety.org. WWW: http://www.mysociety.org
-
-package Cobrands::Emptyhomes::Util;
-use strict;
-use Carp;
-
-sub new {
- my $class = shift;
- return bless {}, $class;
-}
-
-=item
-
-Return the base url for this cobranded site
-
-=cut
-
-sub base_url {
- my $base_url = mySociety::Config::get('BASE_URL');
- if ($base_url !~ /emptyhomes/) {
- $base_url =~ s/http:\/\//http:\/\/emptyhomes\./g;
- }
- return $base_url;
-}
-
-sub admin_base_url {
- return 'https://secure.mysociety.org/admin/emptyhomes/';
-}
-
-sub area_types {
- return qw(DIS LBO MTD UTA LGD COI); # No CTY
-}
-
-=item set_lang_and_domain LANG UNICODE
-
-Set the language and text domain for the site based on the query and host.
-
-=cut
-
-sub set_lang_and_domain {
- my ($self, $lang, $unicode) = @_;
- mySociety::Locale::negotiate_language('en-gb,English,en_GB|cy,Cymraeg,cy_GB', $lang);
- mySociety::Locale::gettext_domain('FixMyStreet-EmptyHomes', $unicode);
- mySociety::Locale::change();
-}
-
-=item site_title
-
-Return the title to be used in page heads
-
-=cut
-
-sub site_title {
- my ($self) = @_;
- return _('Report Empty Homes');
-}
-
-=item feed_xsl
-
-Return the XSL file path to be used for feeds'
-
-=cut
-sub feed_xsl {
- my ($self) = @_;
- return '/xsl.eha.xsl';
-}
-
-1;
-
diff --git a/perllib/Cobrands/Fiksgatami/Util.pm b/perllib/Cobrands/Fiksgatami/Util.pm
deleted file mode 100644
index eb0146ab2..000000000
--- a/perllib/Cobrands/Fiksgatami/Util.pm
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Util.pm:
-# Fiksgatami cobranding for FixMyStreet.
-#
-# Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-
-package Cobrands::Fiksgatami::Util;
-use strict;
-use Carp;
-
-sub new {
- my $class = shift;
- return bless {}, $class;
-}
-
-sub set_lang_and_domain {
- my ($self, $lang, $unicode) = @_;
- mySociety::Locale::negotiate_language('en-gb,English,en_GB|nb,Norwegian,nb_NO', 'nb');
- mySociety::Locale::gettext_domain('FixMyStreet', $unicode);
- mySociety::Locale::change();
-}
-
-# If lat/lon are present in the URL, OpenLayers will use that to centre the map.
-# Need to specify a zoom to stop it defaulting to null/0.
-sub url {
- my ($self, $url) = @_;
- if ($url =~ /lat=/ && $url !~ /zoom=/) {
- $url .= ';zoom=2';
- }
- return $url;
-}
-
-sub enter_postcode_text {
- my ($self, $q) = @_;
- return _('Enter a nearby postcode, or street name and area');
-}
-
-# Is also adding language parameter
-sub disambiguate_location {
- my ($self, $s, $q) = @_;
- $s = "hl=no&gl=no&$s";
- return $s;
-}
-
-sub geocoded_string_check {
- my ($self, $s) = @_;
- return 1 if $s =~ /, Norge/;
- return 0;
-}
-
-sub area_types {
- return ( 'NKO', 'NFY' );
-}
-
-sub area_min_generation {
- return '';
-}
-
-sub admin_base_url {
- return 'http://www.fiksgatami.no/admin/';
-}
-
-sub writetothem_url {
- return 'http://www.norge.no/styresmakter/';
-}
-
-1;
-
diff --git a/perllib/Cobrands/Southampton/Util.pm b/perllib/Cobrands/Southampton/Util.pm
deleted file mode 100644
index d29b53127..000000000
--- a/perllib/Cobrands/Southampton/Util.pm
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Util.pm:
-# Southampton cobranding for FixMyStreet.
-#
-# Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-
-package Cobrands::Southampton::Util;
-use strict;
-use Carp;
-use URI::Escape;
-use mySociety::VotingArea;
-
-sub new {
- my $class = shift;
- return bless {}, $class;
-}
-
-=item site_restriction Q
-
-Return a site restriction clause and a site key.
-
-=cut
-sub site_restriction {
- return ("and council='2567'", 'southampton');
-}
-
-=item
-
-Return the base url for this cobranded site
-
-=cut
-
-sub base_url {
- my $base_url = mySociety::Config::get('BASE_URL');
- if ($base_url !~ /southampton/) {
- $base_url =~ s/http:\/\/(?!www\.)/http:\/\/southampton\./g;
- $base_url =~ s/http:\/\/www\./http:\/\/southampton\./g;
- }
- return $base_url;
-}
-
-=item site_title
-
-Return the title to be used in page heads
-
-=cut
-
-sub site_title {
- my ($self) = @_;
- return 'Southampton City Council FixMyStreet';
-}
-
-sub enter_postcode_text {
- my ($self,$q) = @_;
- return 'Enter a Southampton postcode, or street name and area';
-}
-
-=item council_check COUNCILS QUERY CONTEXT
-
-Return a boolean indicating whether COUNCILS are okay for the location
-in the QUERY, and an error message appropriate to the CONTEXT.
-
-=cut
-
-sub council_check {
- my ($self, $params, $q, $context) = @_;
- my $councils;
- if ($params->{all_councils}) {
- $councils = $params->{all_councils};
- } elsif (defined $params->{lat}) {
- my $parent_types = $mySociety::VotingArea::council_parent_types;
- $councils = mySociety::MaPit::call('point', "4326/$params->{lon},$params->{lat}", type => $parent_types);
- }
- my $council_match = defined $councils->{2567};
- if ($council_match) {
- return 1;
- }
- my $url = 'http://www.fixmystreet.com/';
- $url .= 'alert' if $context eq 'alert';
- $url .= '?pc=' . URI::Escape::uri_escape_utf8($q->param('pc')) if $q->param('pc');
- my $error_msg = "That location is not covered by Southampton.
-Please visit <a href=\"$url\">the main FixMyStreet site</a>.";
- return (0, $error_msg);
-}
-
-# All reports page only has the one council.
-sub all_councils_report {
- return 0;
-}
-
-=item disambiguate_location S Q
-
-Given a string representing a location (street and area expected),
-bias the viewport to around Southampton.
-
-=cut
-
-sub disambiguate_location {
- my ($self, $s, $q) = @_;
- $s = "ll=50.913822,-1.400493&spn=0.084628,0.15701&$s";
- return $s;
-}
-
-sub recent_photos {
- my ($self, $num, $lat, $lon, $dist) = @_;
- $num = 2 if $num == 3;
- return Problems::recent_photos($num, $lat, $lon, $dist);
-}
-
-sub tilma_mid_point {
- return 189;
-}
-
-1;
-
diff --git a/perllib/CrossSell.pm b/perllib/CrossSell.pm
index e40f2166f..44f971462 100644
--- a/perllib/CrossSell.pm
+++ b/perllib/CrossSell.pm
@@ -197,14 +197,14 @@ EOF
# Choose appropriate advert and display it.
# $this_site is to stop a site advertising itself.
sub display_advert ($$;$%) {
- my ($q, $email, $name, %data) = @_;
+ my ($c, $email, $name, %data) = @_;
- return '' unless $q->{site} eq 'fixmystreet';
+ return '' unless $c->cobrand->is_default;
if (defined $data{council} && $data{council} eq '2326') {
my ($out, $ad) = display_hfyc_cheltenham_advert($email, $name);
if ($out) {
- $q->{scratch} = "advert=$ad";
+ $c->stash->{scratch} = "advert=$ad";
return $out;
}
}
@@ -212,17 +212,17 @@ sub display_advert ($$;$%) {
#if ($data{lat}) {
# my $out = display_gny_groups($data{lon}, $data{lat});
# if ($out) {
- # $q->{scratch} = 'advert=gnygroups';
+ # $c->stash->{scratch} = 'advert=gnygroups';
# return '<div style="margin: 0 5em; border-top: dotted 1px #666666;">'
# . $out . '</div>';
# }
#}
- #$q->{scratch} = 'advert=demclub0';
+ #$c->stash->{scratch} = 'advert=demclub0';
#return display_democracyclub();
#unless (defined $data{done_tms} && $data{done_tms}==1) {
- $q->{scratch} = 'advert=news';
+ $c->stash->{scratch} = 'advert=news';
my $auth_signature = '';
unless (defined $data{emailunvalidated} && $data{emailunvalidated}==1) {
$auth_signature = mySociety::AuthToken::sign_with_shared_secret($email, mySociety::Config::get('AUTH_SHARED_SECRET'));
@@ -250,7 +250,7 @@ sub display_advert ($$;$%) {
my $out = &$func($email, $name, $advert_text);
use strict 'refs';
if ($out) {
- $q->{scratch} = "advert=$advert_id";
+ $c->stash->{scratch} = "advert=$advert_id";
return $out;
}
@@ -260,7 +260,7 @@ sub display_advert ($$;$%) {
}
}
- $q->{scratch} = 'advert=pb';
+ $c->stash->{scratch} = 'advert=pb';
return <<EOF;
<div id="advert_thin" style="text-align:center">
<h2 style="font-size: 150%">
diff --git a/perllib/FixMyStreet.pm b/perllib/FixMyStreet.pm
new file mode 100644
index 000000000..d63f708d2
--- /dev/null
+++ b/perllib/FixMyStreet.pm
@@ -0,0 +1,158 @@
+package FixMyStreet;
+
+use strict;
+use warnings;
+
+use Path::Class;
+my $ROOT_DIR = file(__FILE__)->parent->parent->absolute->resolve;
+
+use Readonly;
+
+use mySociety::Config;
+use mySociety::DBHandle;
+
+# load the config file and store the contents in a readonly hash
+mySociety::Config::set_file( __PACKAGE__->path_to("conf/general") );
+Readonly::Hash my %CONFIG, %{ mySociety::Config::get_list() };
+
+=head1 NAME
+
+FixMyStreet
+
+=head1 DESCRIPTION
+
+FixMyStreet is a webite where you can report issues and have them routed to the
+correct authority so that they can be fixed.
+
+Thus module has utility functions for the FMS project.
+
+=head1 METHODS
+
+=head2 test_mode
+
+ FixMyStreet->test_mode( $bool );
+ my $in_test_mode_bool = FixMyStreet->test_mode;
+
+Put the FixMyStreet into test mode - inteded for the unit tests:
+
+ BEGIN {
+ use FixMyStreet;
+ FixMyStreet->test_mode(1);
+ }
+
+=cut
+
+my $TEST_MODE = undef;
+
+sub test_mode {
+ my $class = shift;
+ $TEST_MODE = shift if scalar @_;
+ return $TEST_MODE;
+}
+
+=head2 path_to
+
+ $path = FixMyStreet->path_to( 'conf/general' );
+
+Returns an absolute Path::Class object representing the path to the arguments in
+the FixMyStreet directory.
+
+=cut
+
+sub path_to {
+ my $class = shift;
+ return $ROOT_DIR->file(@_);
+}
+
+=head2 config
+
+ my $config_hash_ref = FixMyStreet->config();
+ my $config_value = FixMyStreet->config($key);
+
+Returns a hashref to the config values. This is readonly so any attempt to
+change it will fail.
+
+Or you can pass it a key and it will return the value for that key, or undef if
+it can't find it.
+
+=cut
+
+sub config {
+ my $class = shift;
+ return \%CONFIG unless scalar @_;
+
+ my $key = shift;
+ return exists $CONFIG{$key} ? $CONFIG{$key} : undef;
+}
+
+=head2 dbic_connect_info
+
+ $connect_info = FixMyStreet->dbic_connect_info();
+
+Returns the array that DBIx::Class::Schema needs to connect to the database.
+Most of the values are read from the config file and others are hordcoded here.
+
+=cut
+
+# for exact details on what this could return refer to:
+#
+# http://search.cpan.org/dist/DBIx-Class/lib/DBIx/Class/Storage/DBI.pm#connect_info
+#
+# we use the one that is most similar to DBI's connect.
+
+# FIXME - should we just use mySociety::DBHandle? will that lead to AutoCommit
+# woes (we want it on, it sets it to off)?
+
+sub dbic_connect_info {
+ my $class = shift;
+ my $config = $class->config;
+
+ my $dsn = "dbi:Pg:dbname=" . $config->{BCI_DB_NAME};
+ $dsn .= ";host=$config->{BCI_DB_HOST}"
+ if $config->{BCI_DB_HOST};
+ $dsn .= ";port=$config->{BCI_DB_PORT}"
+ if $config->{BCI_DB_PORT};
+ $dsn .= ";sslmode=allow";
+
+ my $user = $config->{BCI_DB_USER} || undef;
+ my $password = $config->{BCI_DB_PASS} || undef;
+
+ my $dbi_args = {
+ AutoCommit => 1,
+ pg_enable_utf8 => 1,
+ };
+ my $dbic_args = {};
+
+ return [ $dsn, $user, $password, $dbi_args, $dbic_args ];
+}
+
+=head2 configure_mysociety_dbhandle
+
+ FixMyStreet->configure_mysociety_dbhandle();
+
+Calls configure in mySociety::DBHandle with args from the config. We need to do
+this so that old code that uses mySociety::DBHandle finds it properly set up. We
+can't (might not) be able to share the handle as DBIx::Class wants it with
+AutoCommit on (so that its transaction code can be used in preference to calling
+begin and commit manually) and mySociety::* code does not.
+
+This should be fixed/standardized to avoid having two database handles floating
+around.
+
+=cut
+
+sub configure_mysociety_dbhandle {
+ my $class = shift;
+ my $config = $class->config;
+
+ mySociety::DBHandle::configure(
+ Name => $config->{BCI_DB_NAME},
+ User => $config->{BCI_DB_USER},
+ Password => $config->{BCI_DB_PASS},
+ Host => $config->{BCI_DB_HOST} || undef,
+ Port => $config->{BCI_DB_PORT} || undef,
+ );
+
+}
+
+1;
diff --git a/perllib/FixMyStreet/Alert.pm b/perllib/FixMyStreet/Alert.pm
deleted file mode 100644
index 58a338432..000000000
--- a/perllib/FixMyStreet/Alert.pm
+++ /dev/null
@@ -1,347 +0,0 @@
-#!/usr/bin/perl -w
-#
-# FixMyStreet::Alert.pm
-# Alerts by email or RSS.
-#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/
-#
-# $Id: Alert.pm,v 1.71 2010-01-06 16:50:27 louise Exp $
-
-package FixMyStreet::Alert::Error;
-
-use Error qw(:try);
-
-@FixMyStreet::Alert::Error::ISA = qw(Error::Simple);
-
-package FixMyStreet::Alert;
-
-use strict;
-use Error qw(:try);
-use File::Slurp;
-use FindBin;
-use POSIX qw(strftime);
-use XML::RSS;
-
-use Cobrand;
-use mySociety::AuthToken;
-use mySociety::Config;
-use mySociety::DBHandle qw(dbh);
-use mySociety::Email;
-use mySociety::EmailUtil;
-use mySociety::Gaze;
-use mySociety::Locale;
-use mySociety::MaPit;
-use mySociety::Random qw(random_bytes);
-use mySociety::Sundries qw(ordinal);
-use mySociety::Web qw(ent);
-
-# Add a new alert
-sub create ($$$$;@) {
- my ($email, $alert_type, $cobrand, $cobrand_data, @params) = @_;
- my $already = 0;
- if (0==@params) {
- ($already) = dbh()->selectrow_array('select id from alert where alert_type=? and email=? limit 1',
- {}, $alert_type, $email);
- } elsif (1==@params) {
- ($already) = dbh()->selectrow_array('select id from alert where alert_type=? and email=? and parameter=? limit 1',
- {}, $alert_type, $email, @params);
- } elsif (2==@params) {
- ($already) = dbh()->selectrow_array('select id from alert where alert_type=? and email=? and parameter=? and parameter2=? limit 1',
- {}, $alert_type, $email, @params);
- }
- return $already if $already;
-
- my $id = dbh()->selectrow_array("select nextval('alert_id_seq');");
- my $lang = $mySociety::Locale::lang;
- if (0==@params) {
- dbh()->do('insert into alert (id, alert_type, email, lang, cobrand, cobrand_data)
- values (?, ?, ?, ?, ?, ?)', {}, $id, $alert_type, $email, $lang, $cobrand, $cobrand_data);
- } elsif (1==@params) {
- dbh()->do('insert into alert (id, alert_type, parameter, email, lang, cobrand, cobrand_data)
- values (?, ?, ?, ?, ?, ?, ?)', {}, $id, $alert_type, @params, $email, $lang, $cobrand, $cobrand_data);
- } elsif (2==@params) {
- dbh()->do('insert into alert (id, alert_type, parameter, parameter2, email, lang, cobrand, cobrand_data)
- values (?, ?, ?, ?, ?, ?, ?, ?)', {}, $id, $alert_type, @params, $email, $lang, $cobrand, $cobrand_data);
- }
- dbh()->commit();
- return $id;
-}
-
-sub confirm ($) {
- my $id = shift;
- dbh()->do("update alert set confirmed=1, whendisabled=null where id=?", {}, $id);
- dbh()->commit();
-}
-
-# Delete an alert
-sub delete ($) {
- my $id = shift;
- dbh()->do('update alert set whendisabled = ms_current_timestamp() where id = ?', {}, $id);
- dbh()->commit();
-}
-
-# This makes load of assumptions, but still should be useful
-#
-# Child must have confirmed, id, email, state(!) columns
-# If parent/child, child table must also have name and text
-# and foreign key to parent must be PARENT_id
-
-sub email_alerts ($) {
- my ($testing_email) = @_;
- my $url;
- my $q = dbh()->prepare("select * from alert_type where ref not like '%local_problems%'");
- $q->execute();
- my $testing_email_clause = '';
- while (my $alert_type = $q->fetchrow_hashref) {
- my $ref = $alert_type->{ref};
- my $head_table = $alert_type->{head_table};
- my $item_table = $alert_type->{item_table};
- my $testing_email_clause = "and $item_table.email <> '$testing_email'" if $testing_email;
- my $query = 'select alert.id as alert_id, alert.email as alert_email, alert.lang as alert_lang, alert.cobrand as alert_cobrand,
- alert.cobrand_data as alert_cobrand_data, alert.parameter as alert_parameter, alert.parameter2 as alert_parameter2, ';
- if ($head_table) {
- $query .= "
- $item_table.id as item_id, $item_table.name as item_name, $item_table.text as item_text,
- $head_table.*
- from alert
- inner join $item_table on alert.parameter::integer = $item_table.${head_table}_id
- inner join $head_table on alert.parameter::integer = $head_table.id";
- } else {
- $query .= " $item_table.*,
- $item_table.id as item_id
- from alert, $item_table";
- }
- $query .= "
- where alert_type='$ref' and whendisabled is null and $item_table.confirmed >= whensubscribed
- and $item_table.confirmed >= ms_current_timestamp() - '7 days'::interval
- and (select whenqueued from alert_sent where alert_sent.alert_id = alert.id and alert_sent.parameter::integer = $item_table.id) is null
- and $item_table.email <> alert.email
- $testing_email_clause
- and $alert_type->{item_where}
- and alert.confirmed = 1
- order by alert.id, $item_table.confirmed";
- # XXX Ugh - needs work
- $query =~ s/\?/alert.parameter/ if ($query =~ /\?/);
- $query =~ s/\?/alert.parameter2/ if ($query =~ /\?/);
- $query = dbh()->prepare($query);
- $query->execute();
- my $last_alert_id;
- my %data = ( template => $alert_type->{template}, data => '' );
- while (my $row = $query->fetchrow_hashref) {
- # Cobranded and non-cobranded messages can share a database. In this case, the conf file
- # should specify a vhost to send the reports for each cobrand, so that they don't get sent
- # more than once if there are multiple vhosts running off the same database. The email_host
- # call checks if this is the host that sends mail for this cobrand.
- next unless (Cobrand::email_host($row->{alert_cobrand}));
-
- dbh()->do('insert into alert_sent (alert_id, parameter) values (?,?)', {}, $row->{alert_id}, $row->{item_id});
- if ($last_alert_id && $last_alert_id != $row->{alert_id}) {
- _send_aggregated_alert_email(%data);
- %data = ( template => $alert_type->{template}, data => '' );
- }
-
- # create problem status message for the templates
- $data{state_message} =
- $row->{state} eq 'fixed'
- ? _("This report is currently marked as fixed.")
- : _("This report is currently marked as open.");
-
- $url = Cobrand::base_url_for_emails($row->{alert_cobrand}, $row->{alert_cobrand_data});
- if ($row->{item_text}) {
- $data{problem_url} = $url . "/report/" . $row->{id};
- $data{data} .= $row->{item_name} . ' : ' if $row->{item_name};
- $data{data} .= $row->{item_text} . "\n\n------\n\n";
- } else {
- $data{data} .= $url . "/report/" . $row->{id} . " - $row->{title}\n\n";
- }
- if (!$data{alert_email}) {
- %data = (%data, %$row);
- if ($ref eq 'area_problems' || $ref eq 'council_problems' || $ref eq 'ward_problems') {
- my $va_info = mySociety::MaPit::call('area', $row->{alert_parameter});
- $data{area_name} = $va_info->{name};
- }
- if ($ref eq 'ward_problems') {
- my $va_info = mySociety::MaPit::call('area', $row->{alert_parameter2});
- $data{ward_name} = $va_info->{name};
- }
- }
- $data{cobrand} = $row->{alert_cobrand};
- $data{cobrand_data} = $row->{alert_cobrand_data};
- $data{lang} = $row->{alert_lang};
- $last_alert_id = $row->{alert_id};
- }
- if ($last_alert_id) {
- _send_aggregated_alert_email(%data);
- }
- }
-
- # Nearby done separately as the table contains the parameters
- my $template = dbh()->selectrow_array("select template from alert_type where ref = 'local_problems'");
- my $query = "select * from alert where alert_type='local_problems' and whendisabled is null and confirmed=1 order by id";
- $query = dbh()->prepare($query);
- $query->execute();
- while (my $alert = $query->fetchrow_hashref) {
- next unless (Cobrand::email_host($alert->{cobrand}));
- my $longitude = $alert->{parameter};
- my $latitude = $alert->{parameter2};
- $url = Cobrand::base_url_for_emails($alert->{cobrand}, $alert->{cobrand_data});
- my ($site_restriction, $site_id) = Cobrand::site_restriction($alert->{cobrand}, $alert->{cobrand_data});
- my $d = mySociety::Gaze::get_radius_containing_population($latitude, $longitude, 200000);
- # Convert integer to GB locale string (with a ".")
- $d = mySociety::Locale::in_gb_locale {
- sprintf("%f", int($d*10+0.5)/10);
- };
- my $testing_email_clause = "and problem.email <> '$testing_email'" if $testing_email;
- my %data = ( template => $template, data => '', alert_id => $alert->{id}, alert_email => $alert->{email}, lang => $alert->{lang}, cobrand => $alert->{cobrand}, cobrand_data => $alert->{cobrand_data} );
- my $q = "select * from problem_find_nearby(?, ?, ?) as nearby, problem
- where nearby.problem_id = problem.id and problem.state in ('confirmed', 'fixed')
- and problem.confirmed >= ? and problem.confirmed >= ms_current_timestamp() - '7 days'::interval
- and (select whenqueued from alert_sent where alert_sent.alert_id = ? and alert_sent.parameter::integer = problem.id) is null
- and problem.email <> ?
- $testing_email_clause
- $site_restriction
- order by confirmed desc";
- $q = dbh()->prepare($q);
- $q->execute($latitude, $longitude, $d, $alert->{whensubscribed}, $alert->{id}, $alert->{email});
- while (my $row = $q->fetchrow_hashref) {
- dbh()->do('insert into alert_sent (alert_id, parameter) values (?,?)', {}, $alert->{id}, $row->{id});
- $data{data} .= $url . "/report/" . $row->{id} . " - $row->{title}\n\n";
- }
- _send_aggregated_alert_email(%data) if $data{data};
- }
-}
-
-sub _send_aggregated_alert_email(%) {
- my %data = @_;
- Cobrand::set_lang_and_domain($data{cobrand}, $data{lang}, 1);
-
- $data{unsubscribe_url} = Cobrand::base_url_for_emails($data{cobrand}, $data{cobrand_data}) . '/A/'
- . mySociety::AuthToken::store('alert', { id => $data{alert_id}, type => 'unsubscribe', email => $data{alert_email} } );
- my $template = "$FindBin::Bin/../templates/emails/$data{template}";
- if ($data{cobrand}) {
- my $template_cobrand = "$FindBin::Bin/../templates/emails/$data{cobrand}/$data{template}";
- $template = $template_cobrand if -e $template_cobrand;
- }
- $template = File::Slurp::read_file($template);
- my $sender = Cobrand::contact_email($data{cobrand});
- my $sender_name = Cobrand::contact_name($data{cobrand});
- (my $from = $sender) =~ s/team/fms-DO-NOT-REPLY/; # XXX
- my $email = mySociety::Email::construct_email({
- _template_ => _($template),
- _parameters_ => \%data,
- From => [ $from, _($sender_name) ],
- To => $data{alert_email},
- 'Message-ID' => sprintf('<alert-%s-%s@mysociety.org>', time(), unpack('h*', random_bytes(5, 1))),
- });
-
- my $result = mySociety::EmailUtil::send_email($email, $sender, $data{alert_email});
- if ($result == mySociety::EmailUtil::EMAIL_SUCCESS) {
- dbh()->commit();
- } else {
- dbh()->rollback();
- throw FixMyStreet::Alert::Error("Failed to send alert $data{alert_id}!");
- }
-}
-
-sub generate_rss ($$$;$$$$) {
- my ($type, $xsl, $qs, $db_params, $title_params, $cobrand, $http_q) = @_;
- $db_params ||= [];
- my $url = Cobrand::base_url($cobrand);
- my $cobrand_data = Cobrand::extra_data($cobrand, $http_q);
- my $q = dbh()->prepare('select * from alert_type where ref=?');
- $q->execute($type);
- my $alert_type = $q->fetchrow_hashref;
- my ($site_restriction, $site_id) = Cobrand::site_restriction($cobrand, $cobrand_data);
- throw FixMyStreet::Alert::Error('Unknown alert type') unless $alert_type;
-
- # Do our own encoding
- my $rss = new XML::RSS( version => '2.0', encoding => 'UTF-8',
- stylesheet=> $xsl, encode_output => undef );
- $rss->add_module(prefix=>'georss', uri=>'http://www.georss.org/georss');
-
- # Only apply a site restriction if the alert uses the problem table
- $site_restriction = '' unless $alert_type->{item_table} eq 'problem';
- my $query = 'select * from ' . $alert_type->{item_table} . ' where '
- . ($alert_type->{head_table} ? $alert_type->{head_table}.'_id=? and ' : '')
- . $alert_type->{item_where} . $site_restriction . ' order by '
- . $alert_type->{item_order};
- my $rss_limit = mySociety::Config::get('RSS_LIMIT');
- $query .= " limit $rss_limit" unless $type =~ /^all/;
- $q = dbh()->prepare($query);
- if ($query =~ /\?/) {
- throw FixMyStreet::Alert::Error('Missing parameter') unless @$db_params;
- $q->execute(@$db_params);
- } else {
- $q->execute();
- }
-
- while (my $row = $q->fetchrow_hashref) {
-
- $row->{name} ||= 'anonymous';
-
- my $pubDate;
- if ($row->{confirmed}) {
- $row->{confirmed} =~ /^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)/;
- $pubDate = mySociety::Locale::in_gb_locale {
- strftime("%a, %d %b %Y %H:%M:%S %z", $6, $5, $4, $3, $2-1, $1-1900, -1, -1, 0)
- };
- $row->{confirmed} = strftime("%e %B", $6, $5, $4, $3, $2-1, $1-1900, -1, -1, 0);
- $row->{confirmed} =~ s/^\s+//;
- $row->{confirmed} =~ s/^(\d+)/ordinal($1)/e if $mySociety::Locale::lang eq 'en-gb';
- }
-
- (my $title = _($alert_type->{item_title})) =~ s/{{(.*?)}}/$row->{$1}/g;
- (my $link = $alert_type->{item_link}) =~ s/{{(.*?)}}/$row->{$1}/g;
- (my $desc = _($alert_type->{item_description})) =~ s/{{(.*?)}}/$row->{$1}/g;
- my $cobrand_url = Cobrand::url($cobrand, $url . $link, $http_q);
- my %item = (
- title => ent($title),
- link => $cobrand_url,
- guid => $cobrand_url,
- description => ent(ent($desc)) # Yes, double-encoded, really.
- );
- $item{pubDate} = $pubDate if $pubDate;
- $item{category} = $row->{category} if $row->{category};
-
- my $display_photos = Cobrand::allow_photo_display($cobrand);
- if ($display_photos && $row->{photo}) {
- $item{description} .= ent("\n<br><img src=\"". Cobrand::url($cobrand, $url, $http_q) . "/photo?id=$row->{id}\">");
- }
- my $recipient_name = Cobrand::contact_name($cobrand);
- $item{description} .= ent("\n<br><a href='$cobrand_url'>" .
- sprintf(_("Report on %s"), $recipient_name) . "</a>");
-
- if ($row->{latitude} || $row->{longitude}) {
- $item{georss} = { point => "$row->{latitude} $row->{longitude}" };
- }
- $rss->add_item( %item );
- }
-
- my $row = {};
- if ($alert_type->{head_sql_query}) {
- $q = dbh()->prepare($alert_type->{head_sql_query});
- if ($alert_type->{head_sql_query} =~ /\?/) {
- $q->execute(@$db_params);
- } else {
- $q->execute();
- }
- $row = $q->fetchrow_hashref;
- }
- foreach (keys %$title_params) {
- $row->{$_} = $title_params->{$_};
- }
- (my $title = _($alert_type->{head_title})) =~ s/{{(.*?)}}/$row->{$1}/g;
- (my $link = $alert_type->{head_link}) =~ s/{{(.*?)}}/$row->{$1}/g;
- (my $desc = _($alert_type->{head_description})) =~ s/{{(.*?)}}/$row->{$1}/g;
- $rss->channel(
- title => ent($title), link => "$url$link$qs", description => ent($desc),
- language => 'en-gb'
- );
-
- my $out = $rss->as_string;
- my $uri = Cobrand::url($cobrand, $ENV{SCRIPT_URI}, $http_q);
- $out =~ s{<link>(.*?)</link>}{"<link>" . Cobrand::url($cobrand, $1, $http_q) . "</link><uri>$uri</uri>"}e;
-
- return $out;
-}
diff --git a/perllib/FixMyStreet/App.pm b/perllib/FixMyStreet/App.pm
new file mode 100644
index 000000000..3aa81bd2b
--- /dev/null
+++ b/perllib/FixMyStreet/App.pm
@@ -0,0 +1,394 @@
+package FixMyStreet::App;
+use Moose;
+use namespace::autoclean;
+
+use Catalyst::Runtime 5.80;
+use FixMyStreet;
+use FixMyStreet::Cobrand;
+use Memcached;
+use mySociety::Email;
+use mySociety::EmailUtil;
+use mySociety::Random qw(random_bytes);
+use FixMyStreet::Map;
+
+use URI;
+use URI::QueryParam;
+
+use Catalyst (
+ 'Static::Simple', #
+ 'Unicode::Encoding',
+ 'Session',
+ 'Session::Store::DBIC',
+ 'Session::State::Cookie', # FIXME - we're using our own override atm
+ 'Authentication',
+ 'SmartURI',
+);
+
+extends 'Catalyst';
+
+our $VERSION = '0.01';
+
+__PACKAGE__->config(
+
+ # get the config from the core object
+ %{ FixMyStreet->config() },
+
+ name => 'FixMyStreet::App',
+
+ encoding => 'UTF-8',
+
+ # Disable deprecated behavior needed by old applications
+ disable_component_resolution_regex_fallback => 1,
+
+ # Some generic stuff
+ default_view => 'Web',
+
+ # Serve anything in web dir that is not a .cgi script
+ static => { #
+ include_path => [ FixMyStreet->path_to("web") . "" ],
+ ignore_extensions => ['cgi'],
+ },
+
+ 'Plugin::Session' => { # Catalyst::Plugin::Session::Store::DBIC
+ dbic_class => 'DB::Session',
+ expires => 3600 * 24 * 7 * 6, # 6 months
+ cookie_expires => 0
+ },
+
+ 'Plugin::Authentication' => {
+ default_realm => 'default',
+ default => {
+ credential => { # Catalyst::Authentication::Credential::Password
+ class => 'Password',
+ password_field => 'password',
+ password_type => 'hashed',
+ password_hash_type => 'SHA-1',
+ },
+ store => { # Catalyst::Authentication::Store::DBIx::Class
+ class => 'DBIx::Class',
+ user_model => 'DB::User',
+ },
+ },
+ no_password => { # use post confirm etc
+ credential => { # Catalyst::Authentication::Credential::Password
+ class => 'Password',
+ password_type => 'none',
+ },
+ store => { # Catalyst::Authentication::Store::DBIx::Class
+ class => 'DBIx::Class',
+ user_model => 'DB::User',
+ },
+ },
+ },
+);
+
+# Start the application
+__PACKAGE__->setup();
+
+# set up DB handle for old code
+FixMyStreet->configure_mysociety_dbhandle;
+
+# disable debug logging unless in debug mode
+__PACKAGE__->log->disable('debug') #
+ unless __PACKAGE__->debug;
+
+=head1 NAME
+
+FixMyStreet::App - Catalyst based application
+
+=head1 SYNOPSIS
+
+ script/fixmystreet_app_server.pl
+
+=head1 DESCRIPTION
+
+FixMyStreet.com codebase
+
+=head1 METHODS
+
+=head2 cobrand
+
+ $cobrand = $c->cobrand();
+
+Returns the cobrand object. If not already determined this request finds it and
+caches it to the stash.
+
+=cut
+
+sub cobrand {
+ my $c = shift;
+ return $c->stash->{cobrand} ||= $c->_get_cobrand();
+}
+
+sub _get_cobrand {
+ my $c = shift;
+
+ my $host = $c->req->uri->host;
+ my $override_moniker = $c->get_override('cobrand_moniker');
+
+ my $cobrand_class =
+ $override_moniker
+ ? FixMyStreet::Cobrand->get_class_for_moniker($override_moniker)
+ : FixMyStreet::Cobrand->get_class_for_host($host);
+
+ my $cobrand = $cobrand_class->new( { c => $c } );
+
+ return $cobrand;
+}
+
+=head2 setup_request
+
+ $cobrand = $c->setup_request();
+
+Work out which cobrand we should be using. Set the environment correctly - eg
+template paths, maps, languages etc, etc.
+
+=cut
+
+sub setup_request {
+ my $c = shift;
+
+ $c->setup_dev_overrides();
+
+ my $cobrand = $c->cobrand;
+
+ # append the cobrand templates to the include path
+ $c->stash->{additional_template_paths} =
+ [ $cobrand->path_to_web_templates->stringify ]
+ unless $cobrand->is_default;
+
+ # work out which language to use
+ my $lang_override = $c->get_override('lang');
+ my $host = $c->req->uri->host;
+ my $lang =
+ $lang_override ? $lang_override
+ : $host =~ /^en\./ ? 'en-gb'
+ : $host =~ /cy/ ? 'cy'
+ : undef;
+
+ # set the language and the translation file to use - store it on stash
+ my $set_lang = $cobrand->set_lang_and_domain(
+ $lang, # language
+ 1, # return unicode
+ FixMyStreet->path_to('locale')->stringify # use locale directory
+ );
+ $c->stash->{lang_code} = $set_lang;
+
+ # debug
+ $c->log->debug( sprintf "Set lang to '%s' and cobrand to '%s'",
+ $set_lang, $cobrand->moniker );
+
+ $c->model('DB::Problem')->set_restriction( $cobrand->site_restriction() );
+
+ Memcached::set_namespace( FixMyStreet->config('BCI_DB_NAME') . ":" );
+
+ FixMyStreet::Map::set_map_class( $c->request->param('map') );
+
+ return $c;
+}
+
+=head2 setup_dev_overrides
+
+ $c->setup_dev_overrides();
+
+This is only run if STAGING_SITE is true.
+
+It is intended as an easy way to change the cobrand, language, map etc etc etc
+without having to muck around with domain names and so on. The overrides are set
+by passing _override_xxx parameters in the query. The values and stored in the
+session and are used in preference to the defaults.
+
+All overrides can be easily cleared by setting the _override_clear_all parameter
+to true.
+
+=cut
+
+sub setup_dev_overrides {
+ my $c = shift;
+
+ # If not on STAGING_SITE bail out
+ return unless $c->config->{STAGING_SITE};
+
+ # Extract all the _override_xxx parameters
+ my %params = %{ $c->req->parameters };
+ delete $params{$_} for grep { !m{^_override_} } keys %params;
+
+ # stop if there is nothing to add
+ return 1 unless scalar keys %params;
+
+ # Check to see if we should clear all
+ if ( $params{_override_clear_all} ) {
+ delete $c->session->{overrides};
+ return;
+ }
+
+ # check for all the other _override params and set their values
+ my $overrides = $c->session->{overrides} ||= {};
+ foreach my $raw_key ( keys %params ) {
+ my ($key) = $raw_key =~ m{^_override_(.*)$};
+ $overrides->{$key} = $params{$raw_key};
+ }
+
+ return $overrides;
+}
+
+=head2 get_override
+
+ $value = $c->get_override( 'cobrand_moniker' );
+
+Checks the overrides for the value given and returns it if found, undef if not.
+
+Always returns undef unless on a staging site (avoids autovivifying overrides
+hash in session and so creating a session for all users).
+
+=cut
+
+sub get_override {
+ my ( $c, $key ) = @_;
+ return unless $c->config->{STAGING_SITE};
+ return $c->session->{overrides}->{$key};
+}
+
+=head2 send_email
+
+ $email_sent = $c->send_email( 'email_template.txt', $extra_stash_values );
+
+Send an email by filling in the given template with values in the stash.
+
+You can specify extra values to those already in the stash by passing a hashref
+as the second argument.
+
+The stash (or extra_stash_values) keys 'to', 'from' and 'subject' are used to
+set those fields in the email if they are present.
+
+If a 'from' is not specified then the default from the config is used.
+
+=cut
+
+sub send_email {
+ my $c = shift;
+ my $template = shift;
+ my $extra_stash_values = shift || {};
+
+ my $sender = $c->cobrand->contact_email;
+ my $sender_name = $c->cobrand->contact_name;
+ $sender =~ s/team/fms-DO-NOT-REPLY/;
+
+ # create the vars to pass to the email template
+ my $vars = {
+ from => [ $sender, _($sender_name) ],
+ %{ $c->stash },
+ %$extra_stash_values,
+ additional_template_paths => [
+ FixMyStreet->path_to( 'templates', 'email', $c->cobrand->moniker, $c->stash->{lang_code} )->stringify,
+ FixMyStreet->path_to( 'templates', 'email', $c->cobrand->moniker )->stringify,
+ ]
+ };
+
+ # render the template
+ my $content = $c->view('Email')->render( $c, $template, $vars );
+
+ # create an email - will parse headers out of content
+ my $email = Email::Simple->new($content);
+ $email->header_set( ucfirst($_), $vars->{$_} )
+ for grep { $vars->{$_} } qw( to from subject);
+
+ # pass the email into mySociety::Email to construct the on the wire 7bit
+ # format - this should probably happen in the transport instead but hohum.
+ my $email_text = mySociety::Locale::in_gb_locale { mySociety::Email::construct_email(
+ {
+ _template_ => $email->body, # will get line wrapped
+ _parameters_ => {},
+ $email->header_pairs
+ }
+ ) };
+
+ # send the email
+ $c->model('EmailSend')->send($email_text);
+
+ return $email;
+}
+
+sub send_email_cron {
+ my ( $c, $params, $env_from, $env_to, $nomail ) = @_;
+
+ $params->{'Message-ID'} = sprintf('<fms-cron-%s-%s@mysociety.org>', time(),
+ unpack('h*', random_bytes(5, 1))
+ );
+
+ my $email = mySociety::Locale::in_gb_locale { mySociety::Email::construct_email($params) };
+
+ if ( FixMyStreet->test_mode ) {
+ my $sender = Email::Send->new({ mailer => 'Test' });
+ $sender->send( $email );
+ return 0;
+ } elsif (!$nomail) {
+ return mySociety::EmailUtil::send_email( $email, $env_from, @$env_to );
+ } else {
+ print $email;
+ return 1; # Failure
+ }
+}
+
+=head2 uri_with
+
+ $uri = $c->uri_with( ... );
+
+Simply forwards on to $c->req->uri_with - this is a common typo I make!
+
+=cut
+
+sub uri_with {
+ my $c = shift;
+ return $c->req->uri_with(@_);
+}
+
+=head2 uri_for
+
+ $uri = $c->uri_for( ... );
+
+Like C<uri_for> except that it passes the uri to the cobrand to be altered if
+needed.
+
+=cut
+
+sub uri_for {
+ my $c = shift;
+ my @args = @_;
+
+ my $uri = $c->next::method(@args);
+
+ my $cobranded_uri = $c->cobrand->uri($uri);
+
+ # note that the returned uri may be a string not an object (eg cities)
+ return $cobranded_uri;
+}
+
+=head2 uri_for_email
+
+ $uri = $c->uri_for_email( ... );
+
+Like C<uri_for> except that it checks the cobrand for an email specific url base
+and uses that.
+
+=cut
+
+sub uri_for_email {
+ my $c = shift;
+ my @args = @_;
+
+ my $normal_uri = $c->uri_for(@_)->absolute;
+ my $base = $c->cobrand->base_url_with_lang( 1 );
+
+ my $email_uri = $base . $normal_uri->path_query;
+
+ return URI->new($email_uri);
+}
+
+=head1 SEE ALSO
+
+L<FixMyStreet::App::Controller::Root>, L<Catalyst>
+
+=cut
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm
new file mode 100644
index 000000000..0c428e7a7
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Admin.pm
@@ -0,0 +1,800 @@
+package FixMyStreet::App::Controller::Admin;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+use POSIX qw(strftime strcoll);
+use Digest::MD5 qw(md5_hex);
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Admin- Catalyst Controller
+
+=head1 DESCRIPTION
+
+Admin pages
+
+=head1 METHODS
+
+=cut
+
+=head2 summary
+
+Redirect to index page. There to make the allowed pages stuff neater
+
+=cut
+
+sub begin : Private {
+ my ( $self, $c ) = @_;
+
+ $c->uri_disposition('relative');
+}
+
+sub summary : Path( 'summary' ) : Args(0) {
+ my ( $self, $c ) = @_;
+ $c->go( 'index' );
+}
+
+=head2 index
+
+Displays some summary information for the requests.
+
+=cut
+
+sub index : Path : Args(0) {
+ my ( $self, $c ) = @_;
+
+ $c->forward('check_page_allowed');
+
+ my ( $sql_restriction, $id, $site_restriction ) = $c->cobrand->site_restriction();
+ my $cobrand_restriction = $c->cobrand->moniker eq 'fixmystreet' ? {} : { cobrand => $c->cobrand->moniker };
+
+ my $problems = $c->cobrand->problems->summary_count;
+
+ my %prob_counts =
+ map { $_->state => $_->get_column('state_count') } $problems->all;
+
+ %prob_counts =
+ map { $_ => $prob_counts{$_} || 0 }
+ qw(confirmed fixed unconfirmed hidden partial);
+ $c->stash->{problems} = \%prob_counts;
+ $c->stash->{total_problems_live} =
+ $prob_counts{confirmed} + $prob_counts{fixed};
+
+ my $comments = $c->model('DB::Comment')->summary_count( $site_restriction );
+
+ my %comment_counts =
+ map { $_->state => $_->get_column('state_count') } $comments->all;
+
+ $c->stash->{comments} = \%comment_counts;
+
+ my $alerts = $c->model('DB::Alert')->summary_count( $cobrand_restriction );
+
+ my %alert_counts =
+ map { $_->confirmed => $_->get_column('confirmed_count') } $alerts->all;
+
+ $alert_counts{0} ||= 0;
+ $alert_counts{1} ||= 0;
+
+ $c->stash->{alerts} = \%alert_counts;
+
+ my $contacts = $c->model('DB::Contact')->summary_count( $c->cobrand->contact_restriction );
+
+ my %contact_counts =
+ map { $_->confirmed => $_->get_column('confirmed_count') } $contacts->all;
+
+ $contact_counts{0} ||= 0;
+ $contact_counts{1} ||= 0;
+ $contact_counts{total} = $contact_counts{0} + $contact_counts{1};
+
+ $c->stash->{contacts} = \%contact_counts;
+
+ my $questionnaires = $c->model('DB::Questionnaire')->summary_count( $cobrand_restriction );
+
+ my %questionnaire_counts = map {
+ $_->get_column('answered') => $_->get_column('questionnaire_count')
+ } $questionnaires->all;
+ $questionnaire_counts{1} ||= 0;
+ $questionnaire_counts{0} ||= 0;
+
+ $questionnaire_counts{total} =
+ $questionnaire_counts{0} + $questionnaire_counts{1};
+ $c->stash->{questionnaires_pc} =
+ $questionnaire_counts{total}
+ ? sprintf( '%.1f',
+ $questionnaire_counts{1} / $questionnaire_counts{total} * 100 )
+ : _('n/a');
+ $c->stash->{questionnaires} = \%questionnaire_counts;
+
+ return 1;
+}
+
+sub timeline : Path( 'timeline' ) : Args(0) {
+ my ($self, $c) = @_;
+
+ $c->forward('check_page_allowed');
+
+ my ( $sql_restriction, $id, $site_restriction ) = $c->cobrand->site_restriction();
+ my $cobrand_restriction = { cobrand => $c->cobrand->moniker };
+ my %time;
+
+ $c->model('DB')->schema->storage->sql_maker->quote_char( '"' );
+
+ my $probs = $c->cobrand->problems->timeline;
+
+ foreach ($probs->all) {
+ push @{$time{$_->created->epoch}}, { type => 'problemCreated', date => $_->created_local, obj => $_ };
+ push @{$time{$_->confirmed->epoch}}, { type => 'problemConfirmed', date => $_->confirmed_local, obj => $_ } if $_->confirmed;
+ push @{$time{$_->whensent->epoch}}, { type => 'problemSent', date => $_->whensent_local, obj => $_ } if $_->whensent;
+ }
+
+ my $questionnaires = $c->model('DB::Questionnaire')->timeline( $cobrand_restriction );
+
+ foreach ($questionnaires->all) {
+ push @{$time{$_->whensent->epoch}}, { type => 'quesSent', date => $_->whensent_local, obj => $_ };
+ push @{$time{$_->whenanswered->epoch}}, { type => 'quesAnswered', date => $_->whenanswered_local, obj => $_ } if $_->whenanswered;
+ }
+
+ my $updates = $c->model('DB::Comment')->timeline( $site_restriction );
+
+ foreach ($updates->all) {
+ push @{$time{$_->created->epoch}}, { type => 'update', date => $_->created_local, obj => $_} ;
+ }
+
+ my $alerts = $c->model('DB::Alert')->timeline_created( $cobrand_restriction );
+
+ foreach ($alerts->all) {
+ push @{$time{$_->whensubscribed->epoch}}, { type => 'alertSub', date => $_->whensubscribed_local, obj => $_ };
+ }
+
+ $alerts = $c->model('DB::Alert')->timeline_disabled( $cobrand_restriction );
+
+ foreach ($alerts->all) {
+ push @{$time{$_->whendisabled->epoch}}, { type => 'alertDel', date => $_->whendisabled_local, obj => $_ };
+ }
+
+ $c->model('DB')->schema->storage->sql_maker->quote_char( '' );
+
+ $c->stash->{time} = \%time;
+
+ return 1;
+}
+
+sub questionnaire : Path('questionnaire') : Args(0) {
+ my ( $self, $c ) = @_;
+
+ $c->forward('check_page_allowed');
+
+ my $questionnaires = $c->model('DB::Questionnaire')->search(
+ { whenanswered => \'is not null' }, { group_by => [ 'ever_reported' ], select => [ 'ever_reported', { count => 'me.id' } ], as => [qw/reported questionnaire_count/] }
+ );
+
+
+ my %questionnaire_counts = map { $_->get_column( 'reported' ) => $_->get_column( 'questionnaire_count' ) } $questionnaires->all;
+
+ $questionnaire_counts{1} ||= 0;
+ $questionnaire_counts{0} ||= 0;
+
+ $questionnaire_counts{total} = $questionnaire_counts{0} + $questionnaire_counts{1};
+ $c->stash->{reported_pc} = ( 100 * $questionnaire_counts{1} ) / $questionnaire_counts{total};
+ $c->stash->{not_reported_pc} = ( 100 * $questionnaire_counts{0} ) / $questionnaire_counts{total};
+ $c->stash->{questionnaires} = \%questionnaire_counts;
+
+ return 1;
+}
+
+sub council_list : Path('council_list') : Args(0) {
+ my ( $self, $c ) = @_;
+
+ $c->forward('check_page_allowed');
+
+ my $edit_activity = $c->model('DB::ContactsHistory')->search(
+ undef,
+ {
+ select => [ 'editor', { count => 'contacts_history_id', -as => 'c' } ],
+ group_by => ['editor'],
+ order_by => { -desc => 'c' }
+ }
+ );
+
+ $c->stash->{edit_activity} = $edit_activity;
+
+ my @area_types = $c->cobrand->area_types;
+ my $areas = mySociety::MaPit::call('areas', \@area_types);
+
+ my @councils_ids = sort { strcoll($areas->{$a}->{name}, $areas->{$b}->{name}) } keys %$areas;
+ @councils_ids = $c->cobrand->filter_all_council_ids_list( @councils_ids );
+
+ my $contacts = $c->model('DB::Contact')->search(
+ undef,
+ {
+ select => [ 'area_id', { count => 'id' }, { count => \'case when deleted then 1 else null end' },
+ { count => \'case when confirmed then 1 else null end' } ],
+ as => [qw/area_id c deleted confirmed/],
+ group_by => [ 'area_id' ],
+ result_class => 'DBIx::Class::ResultClass::HashRefInflator'
+ }
+ );
+
+ my %council_info = map { $_->{area_id} => $_ } $contacts->all;
+
+ my @no_info = grep { !$council_info{$_} } @councils_ids;
+ my @one_plus_deleted = grep { $council_info{$_} && $council_info{$_}->{deleted} } @councils_ids;
+ my @unconfirmeds = grep { $council_info{$_} && !$council_info{$_}->{deleted} && $council_info{$_}->{confirmed} != $council_info{$_}->{c} } @councils_ids;
+ my @all_confirmed = grep { $council_info{$_} && !$council_info{$_}->{deleted} && $council_info{$_}->{confirmed} == $council_info{$_}->{c} } @councils_ids;
+
+ $c->stash->{areas} = $areas;
+ $c->stash->{counts} = \%council_info;
+ $c->stash->{no_info} = \@no_info;
+ $c->stash->{one_plus_deleted} = \@one_plus_deleted;
+ $c->stash->{unconfirmeds} = \@unconfirmeds;
+ $c->stash->{all_confirmed} = \@all_confirmed;
+
+ return 1;
+}
+
+sub council_contacts : Path('council_contacts') : Args(1) {
+ my ( $self, $c, $area_id ) = @_;
+
+ $c->forward('check_page_allowed');
+
+ my $posted = $c->req->param('posted') || '';
+ $c->stash->{area_id} = $area_id;
+
+ $c->forward( 'get_token' );
+
+ if ( $posted ) {
+ $c->log->debug( 'posted' );
+ $c->forward('update_contacts');
+ }
+
+ $c->forward('display_contacts');
+
+ return 1;
+}
+
+sub update_contacts : Private {
+ my ( $self, $c ) = @_;
+
+ my $posted = $c->req->param('posted');
+ my $editor = $c->req->remote_user || _('*unknown*');
+
+ if ( $posted eq 'new' ) {
+ $c->forward('check_token');
+
+ my $category = $self->trim( $c->req->param( 'category' ) );
+ my $email = $self->trim( $c->req->param( 'email' ) );
+
+ $category = 'Empty property' if $c->cobrand->moniker eq 'emptyhomes';
+
+ my $contact = $c->model('DB::Contact')->find_or_new(
+ {
+ area_id => $c->stash->{area_id},
+ category => $category,
+ }
+ );
+
+ $contact->email( $email );
+ $contact->confirmed( $c->req->param('confirmed') ? 1 : 0 );
+ $contact->deleted( $c->req->param('deleted') ? 1 : 0 );
+ $contact->note( $c->req->param('note') );
+ $contact->whenedited( \'ms_current_timestamp()' );
+ $contact->editor( $editor );
+
+ if ( $contact->in_storage ) {
+ $c->stash->{updated} = _('Values updated');
+
+ # NB: History is automatically stored by a trigger in the database
+ $contact->update;
+ } else {
+ $c->stash->{updated} = _('New category contact added');
+ $contact->insert;
+ }
+
+ } elsif ( $posted eq 'update' ) {
+ $c->forward('check_token');
+
+ my @categories = $c->req->param('confirmed');
+
+ my $contacts = $c->model('DB::Contact')->search(
+ {
+ area_id => $c->stash->{area_id},
+ category => { -in => \@categories },
+ }
+ );
+
+ $contacts->update(
+ {
+ confirmed => 1,
+ whenedited => \'ms_current_timestamp()',
+ note => 'Confirmed',
+ editor => $editor,
+ }
+ );
+
+ $c->stash->{updated} = _('Values updated');
+ }
+}
+
+sub display_contacts : Private {
+ my ( $self, $c ) = @_;
+
+ $c->forward('setup_council_details');
+
+ my $area_id = $c->stash->{area_id};
+
+ my $contacts = $c->model('DB::Contact')->search(
+ { area_id => $area_id },
+ { order_by => ['category'] }
+ );
+
+ $c->stash->{contacts} = $contacts;
+
+ if ( $c->req->param('text') && $c->req->param('text') == 1 ) {
+ $c->stash->{template} = 'admin/council_contacts.txt';
+ $c->res->content_encoding('text/plain');
+ return 1;
+ }
+
+ return 1;
+}
+
+sub setup_council_details : Private {
+ my ( $self, $c ) = @_;
+
+ my $area_id = $c->stash->{area_id};
+
+ my $mapit_data = mySociety::MaPit::call('area', $area_id);
+
+ $c->stash->{council_name} = $mapit_data->{name};
+
+ my $example_postcode = mySociety::MaPit::call('area/example_postcode', $area_id);
+
+ if ($example_postcode && ! ref $example_postcode) {
+ $c->stash->{example_pc} = $example_postcode;
+ }
+
+ return 1;
+}
+
+sub council_edit : Path('council_edit') : Args(2) {
+ my ( $self, $c, $area_id, $category ) = @_;
+
+ $c->forward('check_page_allowed');
+
+ $c->stash->{area_id} = $area_id;
+
+ $c->forward( 'get_token' );
+ $c->forward('setup_council_details');
+
+ my $contact = $c->model('DB::Contact')->search(
+ {
+ area_id => $area_id,
+ category => $category
+ }
+ )->first;
+
+ $c->stash->{contact} = $contact;
+
+ my $history = $c->model('DB::ContactsHistory')->search(
+ {
+ area_id => $area_id,
+ category => $category
+ },
+ {
+ order_by => ['contacts_history_id']
+ },
+ );
+
+ $c->stash->{history} = $history;
+
+ return 1;
+}
+
+sub search_reports : Path('search_reports') {
+ my ( $self, $c ) = @_;
+
+ $c->forward('check_page_allowed');
+
+ if (my $search = $c->req->param('search')) {
+ $c->stash->{searched} = 1;
+
+ my ( $site_res_sql, $site_key, $site_restriction ) = $c->cobrand->site_restriction;
+
+ my $search_n = 0;
+ $search_n = int($search) if $search =~ /^\d+$/;
+
+ my $like_search = "%$search%";
+
+ # when DBIC creates the join it does 'JOIN users user' in the
+ # SQL which makes PostgreSQL unhappy as user is a reserved
+ # word, hence we need to quote this SQL. However, the quoting
+ # makes PostgreSQL unhappy elsewhere so we only want to do
+ # it for this query and then switch it off afterwards.
+ $c->model('DB')->schema->storage->sql_maker->quote_char( '"' );
+
+ my $problems = $c->cobrand->problems->search(
+ {
+ -or => [
+ 'me.id' => $search_n,
+ 'user.email' => { ilike => $like_search },
+ 'me.name' => { ilike => $like_search },
+ title => { ilike => $like_search },
+ detail => { ilike => $like_search },
+ council => { like => $like_search },
+ cobrand_data => { like => $like_search },
+ ]
+ },
+ {
+ prefetch => 'user',
+ order_by => [\"(state='hidden')",'created']
+ }
+ );
+
+ $c->stash->{problems} = [ $problems->all ];
+
+
+ $c->stash->{edit_council_contacts} = 1
+ if ( grep {$_ eq 'councilcontacts'} keys %{$c->stash->{allowed_pages}});
+
+ my $updates = $c->model('DB::Comment')->search(
+ {
+ -or => [
+ 'me.id' => $search_n,
+ 'problem.id' => $search_n,
+ 'user.email' => { ilike => $like_search },
+ 'me.name' => { ilike => $like_search },
+ text => { ilike => $like_search },
+ 'me.cobrand_data' => { ilike => $like_search },
+ %{ $site_restriction },
+ ]
+ },
+ {
+ -select => [ 'me.*', qw/problem.council problem.state/ ],
+ prefetch => [qw/user problem/],
+ order_by => [\"(me.state='hidden')",\"(problem.state='hidden')",'me.created']
+ }
+ );
+
+ $c->stash->{updates} = [ $updates->all ];
+
+ # Switch quoting back off. See above for explanation of this.
+ $c->model('DB')->schema->storage->sql_maker->quote_char( '' );
+ }
+}
+
+sub report_edit : Path('report_edit') : Args(1) {
+ my ( $self, $c, $id ) = @_;
+
+ my ( $site_res_sql, $site_key, $site_restriction ) = $c->cobrand->site_restriction;
+
+ my $problem = $c->cobrand->problems->search(
+ {
+ id => $id,
+ }
+ )->first;
+
+ $c->detach( '/page_error_404_not_found',
+ [ _('The requested URL was not found on this server.') ] )
+ unless $problem;
+
+ $c->stash->{problem} = $problem;
+
+ $c->forward('get_token');
+ $c->forward('check_page_allowed');
+
+ $c->stash->{updates} =
+ [ $c->model('DB::Comment')
+ ->search( { problem_id => $problem->id }, { order_by => 'created' } )
+ ->all ];
+
+ if ( $c->req->param('resend') ) {
+ $c->forward('check_token');
+
+ $problem->whensent(undef);
+ $problem->update();
+ $c->stash->{status_message} =
+ '<p><em>' . _('That problem will now be resent.') . '</em></p>';
+
+ $c->forward( 'log_edit', [ $id, 'problem', 'resend' ] );
+ }
+ elsif ( $c->req->param('submit') ) {
+ $c->forward('check_token');
+
+ my $done = 0;
+ my $edited = 0;
+
+ my $new_state = $c->req->param('state');
+ my $old_state = $problem->state;
+ if ( $new_state eq 'confirmed'
+ && $problem->state eq 'unconfirmed'
+ && $c->cobrand->moniker eq 'emptyhomes' )
+ {
+ $c->stash->{status_message} =
+ '<p><em>'
+ . _('I am afraid you cannot confirm unconfirmed reports.')
+ . '</em></p>';
+ $done = 1;
+ }
+
+ # do this here so before we update the values in problem
+ if ( $c->req->param('anonymous') ne $problem->anonymous
+ || $c->req->param('name') ne $problem->name
+ || $c->req->param('email') ne $problem->user->email
+ || $c->req->param('title') ne $problem->title
+ || $c->req->param('detail') ne $problem->detail )
+ {
+ $edited = 1;
+ }
+
+ $problem->anonymous( $c->req->param('anonymous') );
+ $problem->title( $c->req->param('title') );
+ $problem->detail( $c->req->param('detail') );
+ $problem->state( $c->req->param('state') );
+ $problem->name( $c->req->param('name') );
+
+ if ( $c->req->param('email') ne $problem->user->email ) {
+ my $user = $c->model('DB::User')->find_or_create(
+ { email => $c->req->param('email') }
+ );
+
+ $user->insert unless $user->in_storage;
+ $problem->user( $user );
+ }
+
+ if ( $c->req->param('remove_photo') ) {
+ $problem->photo(undef);
+ }
+
+ if ( $new_state ne $old_state ) {
+ $problem->lastupdate( \'ms_current_timestamp()' );
+ }
+
+ if ( $new_state eq 'confirmed' and $old_state eq 'unconfirmed' ) {
+ $problem->confirmed( \'ms_current_timestamp()' );
+ }
+
+ if ($done) {
+ $problem->discard_changes;
+ }
+ else {
+ $problem->update;
+
+ if ( $new_state ne $old_state ) {
+ $c->forward( 'log_edit', [ $id, 'problem', 'state_change' ] );
+ }
+ if ($edited) {
+ $c->forward( 'log_edit', [ $id, 'problem', 'edit' ] );
+ }
+
+ $c->stash->{status_message} =
+ '<p><em>' . _('Updated!') . '</em></p>';
+
+ # do this here otherwise lastupdate and confirmed times
+ # do not display correctly
+ $problem->discard_changes;
+ }
+ }
+
+ return 1;
+}
+
+=head2 set_allowed_pages
+
+Sets up the allowed_pages stash entry for checking if the current page is
+available in the current cobrand.
+
+=cut
+
+sub set_allowed_pages : Private {
+ my ( $self, $c ) = @_;
+
+ my $pages = $c->cobrand->admin_pages;
+
+ if( !$pages ) {
+ $pages = {
+ 'summary' => [_('Summary'), 0],
+ 'council_list' => [_('Council contacts'), 1],
+ 'search_reports' => [_('Search Reports'), 2],
+ 'timeline' => [_('Timeline'), 3],
+ 'questionnaire' => [_('Survey Results'), 4],
+ 'council_contacts' => [undef, undef],
+ 'council_edit' => [undef, undef],
+ 'report_edit' => [undef, undef],
+ 'update_edit' => [undef, undef],
+ }
+ }
+
+ my @allowed_links = sort {$pages->{$a}[1] <=> $pages->{$b}[1]} grep {$pages->{$_}->[0] } keys %$pages;
+
+ $c->stash->{allowed_pages} = $pages;
+ $c->stash->{allowed_links} = \@allowed_links;
+
+ return 1;
+}
+
+=item get_token
+
+Generate a token based on user and secret
+
+=cut
+
+sub get_token : Private {
+ my ( $self, $c ) = @_;
+
+ my $secret = $c->model('DB::Secret')->search()->first;
+
+ my $user = $c->req->remote_user();
+ $user ||= '';
+
+ my $token = md5_hex(($user . $secret->secret));
+
+ $c->stash->{token} = $token;
+
+ return 1;
+}
+
+=item check_token
+
+Check that a token has been set on a request and it's the correct token. If
+not then display 404 page
+
+=cut
+
+sub check_token : Private {
+ my ( $self, $c ) = @_;
+
+ if ( $c->req->param('token' ) ne $c->stash->{token} ) {
+ $c->detach( '/page_error_404_not_found', [ _('The requested URL was not found on this server.') ] );
+ }
+
+ return 1;
+}
+
+=item log_edit
+
+ $c->forward( 'log_edit', [ $object_id, $object_type, $action_performed ] );
+
+Adds an entry into the admin_log table using the current remote_user.
+
+=cut
+
+sub log_edit : Private {
+ my ( $self, $c, $id, $object_type, $action ) = @_;
+ $c->model('DB::AdminLog')->create(
+ {
+ admin_user => ( $c->req->remote_user() || '' ),
+ object_type => $object_type,
+ action => $action,
+ object_id => $id,
+ }
+ )->insert();
+}
+
+sub update_edit : Path('update_edit') : Args(1) {
+ my ( $self, $c, $id ) = @_;
+
+ my ( $site_res_sql, $site_key, $site_restriction ) =
+ $c->cobrand->site_restriction;
+ my $update = $c->model('DB::Comment')->search(
+ {
+ id => $id,
+ %{$site_restriction},
+ }
+ )->first;
+
+ $c->detach( '/page_error_404_not_found',
+ [ _('The requested URL was not found on this server.') ] )
+ unless $update;
+
+ $c->forward('get_token');
+ $c->forward('check_page_allowed');
+
+ $c->stash->{update} = $update;
+
+ my $status_message = '';
+ if ( $c->req->param('submit') ) {
+ $c->forward('check_token');
+
+ my $old_state = $update->state;
+ my $new_state = $c->req->param('state');
+
+ my $edited = 0;
+
+ # $update->name can be null which makes ne unhappy
+ my $name = $update->name || '';
+
+ if ( $c->req->param('name') ne $name
+ || $c->req->param('email') ne $update->user->email
+ || $c->req->param('anonymous') ne $update->anonymous
+ || $c->req->param('text') ne $update->text ){
+ $edited = 1;
+ }
+
+ if ( $c->req->param('remove_photo') ) {
+ $update->photo(undef);
+ }
+
+ $update->name( $c->req->param('name') || '' );
+ $update->text( $c->req->param('text') );
+ $update->anonymous( $c->req->param('anonymous') );
+ $update->state( $c->req->param('state') );
+
+ if ( $c->req->param('email') ne $update->user->email ) {
+ my $user =
+ $c->model('DB::User')
+ ->find_or_create( { email => $c->req->param('email') } );
+
+ $user->insert unless $user->in_storage;
+ $update->user($user);
+ }
+
+ $update->update;
+
+ $status_message = '<p><em>' . _('Updated!') . '</em></p>';
+
+ # If we're hiding an update, see if it marked as fixed and unfix if so
+ if ( $new_state eq 'hidden' && $update->mark_fixed ) {
+ if ( $update->problem->state eq 'fixed' ) {
+ $update->problem->state('confirmed');
+ $update->problem->update;
+ }
+
+ $status_message .=
+ '<p><em>' . _('Problem marked as open.') . '</em></p>';
+ }
+
+ if ( $new_state ne $old_state ) {
+ $c->forward( 'log_edit',
+ [ $update->id, 'update', 'state_change' ] );
+ }
+
+ if ($edited) {
+ $c->forward( 'log_edit', [ $update->id, 'update', 'edit' ] );
+ }
+
+ }
+ $c->stash->{status_message} = $status_message;
+
+ return 1;
+}
+
+sub check_page_allowed : Private {
+ my ( $self, $c ) = @_;
+
+ $c->forward('set_allowed_pages');
+
+ (my $page = $c->req->action) =~ s#admin/?##;
+
+ $page ||= 'summary';
+
+ if ( !grep { $_ eq $page } keys %{ $c->stash->{allowed_pages} } ) {
+ $c->detach( '/page_error_404_not_found', [ _('The requested URL was not found on this server.') ] );
+ }
+
+ return 1;
+}
+
+sub trim {
+ my $self = shift;
+ my $e = shift;
+ $e =~ s/^\s+//;
+ $e =~ s/\s+$//;
+ return $e;
+}
+
+=head1 AUTHOR
+
+Struan Donald
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Alert.pm b/perllib/FixMyStreet/App/Controller/Alert.pm
new file mode 100644
index 000000000..e7eab09b8
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Alert.pm
@@ -0,0 +1,506 @@
+package FixMyStreet::App::Controller::Alert;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+use mySociety::EmailUtil qw(is_valid_email);
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Alert - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+=head2 alert
+
+Show the alerts page
+
+=cut
+
+sub index : Path('') : Args(0) {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{cobrand_form_elements} = $c->cobrand->form_elements('alerts');
+
+ unless ( $c->req->referer && $c->req->referer =~ /fixmystreet\.com/ ) {
+ $c->forward( 'add_recent_photos', [10] );
+ }
+}
+
+sub list : Path('list') : Args(0) {
+ my ( $self, $c ) = @_;
+
+ return
+ unless $c->forward('setup_request')
+ && $c->forward('prettify_pc')
+ && $c->forward('determine_location')
+ && $c->forward( 'add_recent_photos', [5] )
+ && $c->forward('setup_council_rss_feeds')
+ && $c->forward('setup_coordinate_rss_feeds');
+}
+
+=head2 subscribe
+
+Target for subscribe form
+
+=cut
+
+sub subscribe : Path('subscribe') : Args(0) {
+ my ( $self, $c ) = @_;
+
+ if ( $c->req->param('rss') ) {
+ $c->detach('rss');
+ }
+ # if it exists then it's been submitted so we should
+ # go to subscribe email and let it work out the next step
+ elsif ( exists $c->req->params->{'rznvy'} ) {
+ $c->detach('subscribe_email');
+ }
+ elsif ( $c->req->params->{'id'} ) {
+ $c->go('updates');
+ }
+
+ # shouldn't get to here but if we have then do something sensible
+ $c->go('index');
+}
+
+=head2 rss
+
+Redirects to relevant RSS feed
+
+=cut
+
+sub rss : Private {
+ my ( $self, $c ) = @_;
+ my $feed = $c->req->params->{feed};
+
+ unless ($feed) {
+ $c->stash->{errors} = [ _('Please select the feed you want') ];
+ $c->go('list');
+ }
+
+ my $url;
+ if ( $feed =~ /^area:(?:\d+:)+(.*)$/ ) {
+ ( my $id = $1 ) =~ tr{:_}{/+};
+ $url = $c->cobrand->base_url() . '/rss/area/' . $id;
+ $c->res->redirect($url);
+ }
+ elsif ( $feed =~ /^(?:council|ward):(?:\d+:)+(.*)$/ ) {
+ ( my $id = $1 ) =~ tr{:_}{/+};
+ $url = $c->cobrand->base_url() . '/rss/reports/' . $id;
+ $c->res->redirect($url);
+ }
+ elsif ( $feed =~ /^local:([\d\.-]+):([\d\.-]+)$/ ) {
+ $url = $c->cobrand->base_url() . '/rss/l/' . $1 . ',' . $2;
+ $c->res->redirect($url);
+ }
+ else {
+ $c->stash->{errors} = [ _('Illegal feed selection') ];
+ $c->go('list');
+ }
+}
+
+=head2 subscribe_email
+
+Sign up to email alerts
+
+=cut
+
+sub subscribe_email : Private {
+ my ( $self, $c ) = @_;
+
+ my $type = $c->req->param('type');
+ $c->stash->{email_type} = 'alert';
+
+ my @errors;
+ push @errors, _('Please enter a valid email address')
+ unless is_valid_email( $c->req->param('rznvy') );
+ push @errors, _('Please select the type of alert you want')
+ if $type && $type eq 'local' && !$c->req->param('feed');
+ if (@errors) {
+ $c->stash->{errors} = \@errors;
+ $c->go('updates') if $type && $type eq 'updates';
+ $c->go('list') if $type && $type eq 'local';
+ $c->go('index');
+ }
+
+ my $email = $c->req->param('rznvy');
+ $c->stash->{email} = $email;
+ $c->forward('process_user');
+
+ if ( $type eq 'updates' ) {
+ $c->forward('set_update_alert_options');
+ }
+ elsif ( $type eq 'local' ) {
+ $c->forward('set_local_alert_options');
+ }
+ else {
+ $c->detach( '/page_error_404_not_found', [ 'Invalid type' ] );
+ }
+
+ $c->forward('create_alert');
+ if ( $c->stash->{alert}->confirmed ) {
+ $c->stash->{confirm_type} = 'created';
+ $c->stash->{template} = 'tokens/confirm_alert.html';
+ } else {
+ $c->forward('send_confirmation_email');
+ }
+}
+
+sub updates : Path('updates') : Args(0) {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{email} = $c->req->param('rznvy');
+ $c->stash->{problem_id} = $c->req->param('id');
+ $c->stash->{cobrand_form_elements} = $c->cobrand->form_elements('alerts');
+}
+
+=head2 confirm
+
+Confirm signup to or unsubscription from an alert. Forwarded here from Tokens.
+
+=cut
+
+sub confirm : Private {
+ my ( $self, $c ) = @_;
+
+ my $alert = $c->stash->{alert};
+
+ if ( $c->stash->{confirm_type} eq 'subscribe' ) {
+ $alert->confirm();
+ }
+ elsif ( $c->stash->{confirm_type} eq 'unsubscribe' ) {
+ $alert->disable();
+ }
+}
+
+=head2 create_alert
+
+Take the alert options from the stash and use these to create a new
+alert. If it finds an existing alert that's the same then use that
+
+=cut
+
+sub create_alert : Private {
+ my ( $self, $c ) = @_;
+
+ my $options = $c->stash->{alert_options};
+
+ my $alert = $c->model('DB::Alert')->find($options);
+
+ unless ($alert) {
+ $options->{cobrand} = $c->cobrand->moniker();
+ $options->{cobrand_data} = $c->cobrand->extra_update_data();
+ $options->{lang} = $c->stash->{lang_code};
+
+ if ( $c->user && $c->stash->{alert_user}->in_storage && $c->user->id == $c->stash->{alert_user}->id ) {
+ $options->{confirmed} = 1;
+ }
+
+ $alert = $c->model('DB::Alert')->new($options);
+ $alert->insert();
+ }
+
+ $c->stash->{alert} = $alert;
+}
+
+=head2 set_update_alert_options
+
+Set up the options in the stash required to create a problem update alert
+
+=cut
+
+sub set_update_alert_options : Private {
+ my ( $self, $c ) = @_;
+
+ my $report_id = $c->req->param('id');
+
+ my $options = {
+ user => $c->stash->{alert_user},
+ alert_type => 'new_updates',
+ parameter => $report_id,
+ };
+
+ $c->stash->{alert_options} = $options;
+}
+
+=head2 set_local_alert_options
+
+Set up the options in the stash required to create a local problems alert
+
+=cut
+
+sub set_local_alert_options : Private {
+ my ( $self, $c ) = @_;
+
+ my $feed = $c->req->param('feed');
+
+ my ( $type, @params, $alert );
+ if ( $feed =~ /^area:(?:\d+:)?(\d+)/ ) {
+ $type = 'area_problems';
+ push @params, $1;
+ }
+ elsif ( $feed =~ /^council:(\d+)/ ) {
+ $type = 'council_problems';
+ push @params, $1, $1;
+ }
+ elsif ( $feed =~ /^ward:(\d+):(\d+)/ ) {
+ $type = 'ward_problems';
+ push @params, $1, $2;
+ }
+ elsif ( $feed =~
+ m{ \A local: ( [\+\-]? \d+ \.? \d* ) : ( [\+\-]? \d+ \.? \d* ) }xms )
+ {
+ $type = 'local_problems';
+ push @params, $2, $1; # Note alert parameters are lon,lat
+ }
+
+ my $options = {
+ user => $c->stash->{alert_user},
+ alert_type => $type
+ };
+
+ if ( scalar @params == 1 ) {
+ $options->{parameter} = $params[0];
+ }
+ elsif ( scalar @params == 2 ) {
+ $options->{parameter} = $params[0];
+ $options->{parameter2} = $params[1];
+ }
+
+ $c->stash->{alert_options} = $options;
+}
+
+=head2 send_confirmation_email
+
+Generate a token and send out an alert subscription confirmation email and
+then display confirmation page.
+
+=cut
+
+sub send_confirmation_email : Private {
+ my ( $self, $c ) = @_;
+
+ my $token = $c->model("DB::Token")->create(
+ {
+ scope => 'alert',
+ data => {
+ id => $c->stash->{alert}->id,
+ type => 'subscribe',
+ email => $c->stash->{alert}->user->email
+ }
+ }
+ );
+
+ $c->stash->{token_url} = $c->uri_for_email( '/A', $token->token );
+
+ $c->send_email( 'alert-confirm.txt', { to => $c->stash->{alert}->user->email } );
+
+ $c->stash->{template} = 'email_sent.html';
+}
+
+=head2 prettify_pc
+
+This will canonicalise and prettify the postcode and stick a pretty_pc and pretty_pc_text in the stash.
+
+=cut
+
+sub prettify_pc : Private {
+ my ( $self, $c ) = @_;
+
+ my $pretty_pc = $c->req->params->{'pc'};
+
+ if ( mySociety::PostcodeUtil::is_valid_postcode( $c->req->params->{'pc'} ) )
+ {
+ $pretty_pc = mySociety::PostcodeUtil::canonicalise_postcode(
+ $c->req->params->{'pc'} );
+ my $pretty_pc_text = $pretty_pc;
+ $pretty_pc_text =~ s/ //g;
+ $c->stash->{pretty_pc_text} = $pretty_pc_text;
+ }
+
+ $c->stash->{pretty_pc} = $pretty_pc;
+
+ return 1;
+}
+
+sub process_user : Private {
+ my ( $self, $c ) = @_;
+
+ my $email = $c->stash->{email};
+ my $alert_user = $c->model('DB::User')->find_or_new( { email => $email } );
+ $c->stash->{alert_user} = $alert_user;
+}
+
+=head2 setup_coordinate_rss_feeds
+
+Takes the latitide and longitude from the stash and uses them to generate uris
+for the local rss feeds
+
+=cut
+
+sub setup_coordinate_rss_feeds : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{rss_feed_id} =
+ sprintf( 'local:%s:%s', $c->stash->{latitude}, $c->stash->{longitude} );
+
+ my $rss_feed;
+ if ( $c->stash->{pretty_pc_text} ) {
+ $rss_feed = $c->uri_for( "/rss/pc/" . $c->stash->{pretty_pc_text} );
+ }
+ else {
+ $rss_feed = $c->uri_for(
+ sprintf( "/rss/l/%s,%s",
+ $c->stash->{latitude},
+ $c->stash->{longitude} )
+ );
+ }
+
+ $c->stash->{rss_feed_uri} = $rss_feed;
+
+ $c->stash->{rss_feed_2k} = $c->uri_for( $rss_feed . '/2' );
+ $c->stash->{rss_feed_5k} = $c->uri_for( $rss_feed . '/5' );
+ $c->stash->{rss_feed_10k} = $c->uri_for( $rss_feed . '/10' );
+ $c->stash->{rss_feed_20k} = $c->uri_for( $rss_feed . '/20' );
+
+ return 1;
+}
+
+=head2 setup_council_rss_feeds
+
+Generate the details required to display the council/ward/area RSS feeds
+
+=cut
+
+sub setup_council_rss_feeds : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{council_check_action} = 'alert';
+ unless ( $c->forward('/council/load_and_check_councils_and_wards') ) {
+ $c->go('index');
+ }
+
+ ( $c->stash->{options}, $c->stash->{reported_to_options} ) =
+ $c->cobrand->council_rss_alert_options( $c->stash->{all_councils}, $c );
+
+ return 1;
+}
+
+=head2 determine_location
+
+Do all the things we need to do to work out where the alert is for
+and to setup the location related things for later
+
+=cut
+
+sub determine_location : Private {
+ my ( $self, $c ) = @_;
+
+ # Try to create a location for whatever we have
+ unless ( $c->forward('/location/determine_location_from_coords')
+ || $c->forward('/location/determine_location_from_pc') )
+ {
+
+ if ( $c->stash->{possible_location_matches} ) {
+ $c->stash->{choose_target_uri} = $c->uri_for('/alert/list');
+ $c->detach('choose');
+ }
+
+ $c->go('index') if $c->stash->{location_error};
+ }
+
+ # truncate the lat,lon for nicer urls
+ ( $c->stash->{latitude}, $c->stash->{longitude} ) =
+ map { Utils::truncate_coordinate($_) }
+ ( $c->stash->{latitude}, $c->stash->{longitude} );
+
+ my $dist =
+ mySociety::Gaze::get_radius_containing_population( $c->stash->{latitude},
+ $c->stash->{longitude}, 200000 );
+ $dist = int( $dist * 10 + 0.5 );
+ $dist = $dist / 10.0;
+ $c->stash->{population_radius} = $dist;
+
+ return 1;
+}
+
+=head2 add_recent_photos
+
+ $c->forward( 'add_recent_photos', [ $num_photos ] );
+
+Adds the most recent $num_photos to the template. If there is coordinate
+and population radius information in the stash uses that to limit it.
+
+=cut
+
+sub add_recent_photos : Private {
+ my ( $self, $c, $num_photos ) = @_;
+
+ if ( $c->stash->{latitude}
+ and $c->stash->{longitude}
+ and $c->stash->{population_radius} )
+ {
+
+ $c->stash->{photos} = $c->cobrand->recent_photos(
+ $num_photos,
+ $c->stash->{latitude},
+ $c->stash->{longitude},
+ $c->stash->{population_radius}
+ );
+ }
+ else {
+ $c->stash->{photos} = $c->cobrand->recent_photos($num_photos);
+ }
+
+ return 1;
+}
+
+sub choose : Private {
+ my ( $self, $c ) = @_;
+ $c->stash->{template} = 'alert/choose.html';
+}
+
+
+=head2 setup_request
+
+Setup the variables we need for the rest of the request
+
+=cut
+
+sub setup_request : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{rznvy} = $c->req->param('rznvy');
+ $c->stash->{selected_feed} = $c->req->param('feed');
+
+ if ( $c->user ) {
+ $c->stash->{rznvy} ||= $c->user->email;
+ }
+
+ $c->stash->{cobrand_form_elements} = $c->cobrand->form_elements('alerts');
+
+ return 1;
+}
+
+=head1 AUTHOR
+
+Struan Donald
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Around.pm b/perllib/FixMyStreet/App/Controller/Around.pm
new file mode 100644
index 000000000..82d920b3e
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Around.pm
@@ -0,0 +1,300 @@
+package FixMyStreet::App::Controller::Around;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+use FixMyStreet::Map;
+use List::MoreUtils qw(any);
+use Encode;
+use FixMyStreet::Map;
+use Utils;
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Around - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Allow the user to search for reports around a particular location.
+
+=head1 METHODS
+
+=head2 around
+
+Find the location search and display nearby reports (for pc or lat,lon).
+
+For x,y searches convert to lat,lon and 301 redirect to them.
+
+If no search redirect back to the homepage.
+
+=cut
+
+sub around_index : Path : Args(0) {
+ my ( $self, $c ) = @_;
+
+ # handle old coord systems
+ $c->forward('redirect_en_or_xy_to_latlon');
+
+ # Check if we have a partial report
+ my $partial_report = $c->forward('load_partial');
+
+ # Try to create a location for whatever we have
+ return
+ unless $c->forward('/location/determine_location_from_coords')
+ || $c->forward('/location/determine_location_from_pc');
+
+ # Check to see if the spot is covered by a council - if not show an error.
+ return unless $c->forward('check_location_is_acceptable');
+
+ # If we have a partial - redirect to /report/new so that it can be
+ # completed.
+ if ($partial_report) {
+ my $new_uri = $c->uri_for(
+ '/report/new',
+ {
+ partial => $c->stash->{partial_token}->token,
+ latitude => $c->stash->{latitude},
+ longitude => $c->stash->{longitude},
+ pc => $c->stash->{pc},
+ }
+ );
+ return $c->res->redirect($new_uri);
+ }
+
+ # Show the nearby reports
+ $c->detach('display_location');
+}
+
+=head2 redirect_en_or_xy_to_latlon
+
+ # detaches if there was a redirect
+ $c->forward('redirect_en_or_xy_to_latlon');
+
+Handle coord systems that are no longer in use.
+
+=cut
+
+sub redirect_en_or_xy_to_latlon : Private {
+ my ( $self, $c ) = @_;
+ my $req = $c->req;
+
+ # check for x,y or e,n requests
+ my $x = $req->param('x');
+ my $y = $req->param('y');
+ my $e = $req->param('e');
+ my $n = $req->param('n');
+
+ # lat and lon - fill in below if we need to
+ my ( $lat, $lon );
+
+ if ( $x || $y ) {
+ ( $lat, $lon ) = FixMyStreet::Map::tile_xy_to_wgs84( $x, $y );
+ }
+ elsif ( $e || $n ) {
+ ( $lat, $lon ) = Utils::convert_en_to_latlon_truncated( $e, $n );
+ }
+ else {
+ return;
+ }
+
+ # create a uri and redirect to it
+ my $ll_uri = $c->uri_for( '/around', { lat => $lat, lon => $lon } );
+ $c->res->redirect( $ll_uri, 301 );
+ $c->detach;
+}
+
+=head2 load_partial
+
+ my $partial_report = $c->forward('load_partial');
+
+Check for the partial token and load the partial report. If found save it and
+token to stash and return report. Otherwise return false.
+
+=cut
+
+sub load_partial : Private {
+ my ( $self, $c ) = @_;
+
+ my $partial = scalar $c->req->param('partial')
+ || return;
+
+ # is it in the database
+ my $token =
+ $c->model("DB::Token")
+ ->find( { scope => 'partial', token => $partial } ) #
+ || last;
+
+ # can we get an id from it?
+ my $report_id = $token->data #
+ || last;
+
+ # load the related problem
+ my $report = $c->cobrand->problems #
+ ->search( { id => $report_id, state => 'partial' } ) #
+ ->first
+ || last;
+
+ # save what we found on the stash.
+ $c->stash->{partial_token} = $token;
+ $c->stash->{partial_report} = $report;
+
+ return $report;
+}
+
+=head2 display_location
+
+Display a specific lat/lng location (which may have come from a pc search).
+
+=cut
+
+sub display_location : Private {
+ my ( $self, $c ) = @_;
+
+ # set the template to use
+ $c->stash->{template} = 'around/display_location.html';
+
+ # get the lat,lng
+ my $latitude = $c->stash->{latitude};
+ my $longitude = $c->stash->{longitude};
+
+ # truncate the lat,lon for nicer rss urls, and strings for outputting
+ my $short_latitude = Utils::truncate_coordinate($latitude);
+ my $short_longitude = Utils::truncate_coordinate($longitude);
+ $c->stash->{short_latitude} = $short_latitude;
+ $c->stash->{short_longitude} = $short_longitude;
+
+ # Deal with pin hiding/age
+ my $all_pins = $c->req->param('all_pins') ? 1 : undef;
+ $c->stash->{all_pins} = $all_pins;
+ my $interval = $all_pins ? undef : $c->cobrand->on_map_default_max_pin_age;
+
+ # get the map features
+ my ( $on_map_all, $on_map, $around_map, $distance ) =
+ FixMyStreet::Map::map_features( $c, $latitude, $longitude,
+ $interval );
+
+ # copy the found reports to the stash
+ $c->stash->{on_map} = $on_map;
+ $c->stash->{around_map} = $around_map;
+ $c->stash->{distance} = $distance;
+
+ # create a list of all the pins
+ my @pins;
+ unless ($c->req->param('no_pins')) {
+ @pins = map {
+ # Here we might have a DB::Problem or a DB::Nearby, we always want the problem.
+ my $p = (ref $_ eq 'FixMyStreet::App::Model::DB::Nearby') ? $_->problem : $_;
+ {
+ latitude => $p->latitude,
+ longitude => $p->longitude,
+ colour => $p->state eq 'fixed' ? 'green' : 'red',
+ id => $p->id,
+ title => $p->title,
+ }
+ } @$on_map_all, @$around_map;
+ }
+
+ FixMyStreet::Map::display_map(
+ $c,
+ latitude => $latitude,
+ longitude => $longitude,
+ clickable => 1,
+ pins => \@pins,
+ );
+
+ return 1;
+}
+
+=head2 check_location_is_acceptable
+
+Find the lat and lon in stash and check that they are acceptable to the council,
+and that they are in UK (if we are in UK).
+
+=cut
+
+sub check_location_is_acceptable : Private {
+ my ( $self, $c ) = @_;
+
+ # These should be set now
+ my $lat = $c->stash->{latitude};
+ my $lon = $c->stash->{longitude};
+
+ # If in UK and we have a lat,lon coocdinate check it is in UK
+ if ( $lat && $c->config->{COUNTRY} eq 'GB' ) {
+ eval { Utils::convert_latlon_to_en( $lat, $lon ); };
+ if ($@) {
+ $c->stash->{location_error} =
+ _( "We had a problem with the supplied co-ordinates - outside the UK?" );
+ return;
+ }
+ }
+
+ # check that there are councils that can accept this location
+ $c->stash->{council_check_action} = 'submit_problem';
+ $c->stash->{remove_redundant_councils} = 1;
+ return $c->forward('/council/load_and_check_councils');
+}
+
+=head2 /ajax
+
+Handle the ajax calls that the map makes when it is dragged. The info returned
+is used to update the pins on the map and the text descriptions on the side of
+the map.
+
+=cut
+
+sub ajax : Path('/ajax') {
+ my ( $self, $c ) = @_;
+
+ # Our current X/Y middle of visible map
+ my $x = ( $c->req->param('x') || 0 ) + 0;
+ my $y = ( $c->req->param('y') || 0 ) + 0;
+
+ # Where we started as that's the (0,0) we have to work to
+ my $sx = ( $c->req->param('sx') || 0 ) + 0;
+ my $sy = ( $c->req->param('sy') || 0 ) + 0;
+
+ # how far back should we go?
+ my $all_pins = $c->req->param('all_pins') ? 1 : undef;
+ my $interval = $all_pins ? undef : $c->cobrand->on_map_default_max_pin_age;
+
+ # extract the data from the map
+ my ( $pins, $on_map, $around_map, $dist ) =
+ FixMyStreet::Map::map_pins( $c, $x, $y, $sx, $sy, $interval );
+
+ # render templates to get the html
+ # my $on_map_list_html = $c->forward(
+ # "View::Web", "render",
+ my $on_map_list_html =
+ $c->view('Web')
+ ->render( $c, 'around/on_map_list_items.html', { on_map => $on_map } );
+
+ # my $around_map_list_html = $c->forward(
+ # "View::Web", "render",
+ my $around_map_list_html = $c->view('Web')->render(
+ $c,
+ 'around/around_map_list_items.html',
+ { around_map => $around_map, dist => $dist }
+ );
+
+ # JSON encode the response
+ my $body = JSON->new->utf8(1)->pretty(1)->encode(
+ {
+ pins => $pins,
+ current => $on_map_list_html,
+ current_near => $around_map_list_html,
+ }
+ );
+
+ # assume this is not cacheable - may need to be more fine-grained later
+ $c->res->content_type('text/javascript; charset=utf-8');
+ $c->res->header( 'Cache_Control' => 'max-age=0' );
+
+ # Set the body - note that the js needs the surrounding brackets.
+ $c->res->body("($body)");
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm
new file mode 100644
index 000000000..7526c2c25
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Auth.pm
@@ -0,0 +1,240 @@
+package FixMyStreet::App::Controller::Auth;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+use Email::Valid;
+use Net::Domain::TLD;
+use mySociety::AuthToken;
+use Digest::SHA1 qw(sha1_hex);
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Auth - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Controller for all the authentication related pages - create account, login,
+logout.
+
+=head1 METHODS
+
+=head2 index
+
+Present the user with a login / create account page.
+
+=cut
+
+sub general : Path : Args(0) {
+ my ( $self, $c ) = @_;
+ my $req = $c->req;
+
+ # all done unless we have a form posted to us
+ return unless $req->method eq 'POST';
+
+ # decide which action to take
+ $c->detach('email_login') if $req->param('email_login');
+ $c->detach('login'); # default
+
+}
+
+=head2 login
+
+Allow the user to legin with a username and a password.
+
+=cut
+
+sub login : Private {
+ my ( $self, $c ) = @_;
+
+ my $email = $c->req->param('email') || '';
+ my $password = $c->req->param('password') || '';
+ my $remember_me = $c->req->param('remember_me') || 0;
+
+ # logout just in case
+ $c->logout();
+
+ if ( $email
+ && $password
+ && $c->authenticate( { email => $email, password => $password } ) )
+ {
+
+ # unless user asked to be remembered limit the session to browser
+ $c->set_session_cookie_expire(0)
+ unless $remember_me;
+
+ $c->res->redirect( $c->uri_for('/my') );
+ return;
+ }
+
+ # could not authenticate - show an error
+ $c->stash->{login_error} = 1;
+}
+
+=head2 email_login
+
+Email the user the details they need to log in. Don't check for an account - if
+there isn't one we can create it when they come back with a token (which
+contains the email addresss).
+
+=cut
+
+sub email_login : Private {
+ my ( $self, $c ) = @_;
+
+ # check that the email is valid - otherwise flag an error
+ my $raw_email = lc( $c->req->param('email') || '' );
+
+ my $email_checker = Email::Valid->new(
+ -mxcheck => 1,
+ -tldcheck => 1,
+ -fqdn => 1,
+ );
+
+ my $good_email = $email_checker->address($raw_email);
+ if ( !$good_email ) {
+ $c->stash->{email} = $raw_email;
+ $c->stash->{email_error} =
+ $raw_email ? $email_checker->details : 'missing';
+ return;
+ }
+
+ my $token_obj = $c->model('DB::Token') #
+ ->create(
+ {
+ scope => 'email_login',
+ data => { email => $good_email }
+ }
+ );
+
+ # log the user in, send them an email and redirect to the welcome page
+ $c->stash->{token} = $token_obj->token;
+ $c->send_email( 'login.txt', { to => $good_email } );
+ $c->res->redirect( $c->uri_for('token') );
+}
+
+=head2 token
+
+Handle the 'email_login' tokens. Find the account for the email address
+(creating if needed), authenticate the user and delete the token.
+
+=cut
+
+sub token : Local {
+ my ( $self, $c, $url_token ) = @_;
+
+ # check for a token - if none found then return
+ return unless $url_token;
+
+ # retrieve the token or return
+ my $token_obj =
+ $c->model('DB::Token')
+ ->find( { scope => 'email_login', token => $url_token, } );
+
+ if ( !$token_obj ) {
+ $c->stash->{token_not_found} = 1;
+ return;
+ }
+
+ # logout in case we are another user
+ $c->logout();
+
+ # get the email and scrap the token
+ my $email = $token_obj->data->{email};
+ $token_obj->delete;
+
+ # find or create the user related to the token and delete the token
+ my $user = $c->model('DB::User')->find_or_create( { email => $email } );
+ $c->authenticate( { email => $user->email }, 'no_password' );
+
+ # send the user to their page
+ $c->res->redirect( $c->uri_for('/my') );
+}
+
+=head2 change_password
+
+Let the user change their password.
+
+=cut
+
+sub change_password : Local {
+ my ( $self, $c ) = @_;
+
+ # FIXME - handle not being logged in more elegantly
+ unless ( $c->user ) {
+ $c->res->redirect( $c->uri_for('/auth') );
+ $c->detach;
+ }
+
+ # FIXME - CSRF check here
+ # FIXME - minimum criteria for passwords (length, contain number, etc)
+
+ # If not a post then no submission
+ return unless $c->req->method eq 'POST';
+
+ # get the passwords
+ my $new = $c->req->param('new_password') // '';
+ my $confirm = $c->req->param('confirm') // '';
+
+ # check for errors
+ my $password_error =
+ !$new && !$confirm ? 'missing'
+ : $new ne $confirm ? 'mismatch'
+ : '';
+
+ if ($password_error) {
+ $c->stash->{password_error} = $password_error;
+ $c->stash->{new_password} = $new;
+ $c->stash->{confirm} = $confirm;
+ return;
+ }
+
+ # we should have a usable password - save it to the user
+ $c->user->obj->update( { password => sha1_hex($new) } );
+ $c->stash->{password_changed} = 1;
+
+}
+
+=head2 logout
+
+Log the user out. Tell them we've done so.
+
+=cut
+
+sub logout : Local {
+ my ( $self, $c ) = @_;
+ $c->logout();
+}
+
+=head2 check_auth
+
+Utility page - returns a simple message 'OK' and a 200 response if the user is
+authenticated and a 'Unauthorized' / 401 reponse if they are not.
+
+Mainly intended for testing but might also be useful for ajax calls.
+
+=cut
+
+sub check_auth : Local {
+ my ( $self, $c ) = @_;
+
+ # choose the response
+ my ( $body, $code ) #
+ = $c->user
+ ? ( 'OK', 200 )
+ : ( 'Unauthorized', 401 );
+
+ # set the response
+ $c->res->body($body);
+ $c->res->code($code);
+
+ # NOTE - really a 401 response should also contain a 'WWW-Authenticate'
+ # header but we ignore that here. The spec is not keeping up with usage.
+
+ return;
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Contact.pm b/perllib/FixMyStreet/App/Controller/Contact.pm
new file mode 100644
index 000000000..047de7691
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Contact.pm
@@ -0,0 +1,237 @@
+package FixMyStreet::App::Controller::Contact;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Contact - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Contact us page
+
+=head1 METHODS
+
+=cut
+
+=head2 index
+
+Display contact us page
+
+=cut
+
+sub index : Path : Args(0) {
+ my ( $self, $c ) = @_;
+
+ return
+ unless $c->forward('setup_request')
+ && $c->forward('determine_contact_type');
+}
+
+=head2 submit
+
+Handle contact us form submission
+
+=cut
+
+sub submit : Path('submit') : Args(0) {
+ my ( $self, $c ) = @_;
+
+ return
+ unless $c->forward('setup_request')
+ && $c->forward('determine_contact_type')
+ && $c->forward('validate')
+ && $c->forward('prepare_params_for_email')
+ && $c->forward('send_email');
+}
+
+=head2 determine_contact_type
+
+Work out if we have got here via a report/update or this is a
+generic contact request and set up things accordingly
+
+=cut
+
+sub determine_contact_type : Private {
+ my ( $self, $c ) = @_;
+
+ my $id = $c->req->param('id');
+ my $update_id = $c->req->param('update_id');
+ $id = undef unless $id && $id =~ /^[1-9]\d*$/;
+ $update_id = undef unless $update_id && $update_id =~ /^[1-9]\d*$/;
+
+ if ($id) {
+
+ $c->forward( '/report/load_problem_or_display_error', [ $id ] );
+
+ if ($update_id) {
+ my $update = $c->model('DB::Comment')->find(
+ { id => $update_id }
+ );
+
+ $c->stash->{update} = $update;
+ }
+ }
+
+ return 1;
+}
+
+=head2 validate
+
+Validate the form submission parameters. Sets error messages and redirect
+to index page if errors.
+
+=cut
+
+sub validate : Private {
+ my ( $self, $c ) = @_;
+
+ my ( %field_errors, @errors );
+ my %required = (
+ name => _('Please enter your name'),
+ em => _('Please enter your email'),
+ subject => _('Please enter a subject'),
+ message => _('Please write a message')
+ );
+
+ foreach my $field ( keys %required ) {
+ $field_errors{$field} = $required{$field}
+ unless $c->req->param($field) =~ /\S/;
+ }
+
+ unless ( $field_errors{em} ) {
+ $field_errors{em} = _('Please enter a valid email address')
+ if !mySociety::EmailUtil::is_valid_email( $c->req->param('em') );
+ }
+
+ push @errors, _('Illegal ID')
+ if $c->req->param('id') && $c->req->param('id') !~ /^[1-9]\d*$/
+ or $c->req->param('update_id')
+ && $c->req->param('update_id') !~ /^[1-9]\d*$/;
+
+ unshift @errors,
+ _('There were problems with your report. Please see below.')
+ if scalar keys %field_errors;
+
+ if ( @errors or scalar keys %field_errors ) {
+ $c->stash->{errors} = \@errors;
+ $c->stash->{field_errors} = \%field_errors;
+ $c->go('index');
+ }
+
+ return 1;
+}
+
+=head2 prepare_params_for_email
+
+Does neccessary reformating of exiting params and add any additional
+information required for emailing ( problem ids, admin page links etc )
+
+=cut
+
+sub prepare_params_for_email : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{message} =~ s/\r\n/\n/g;
+ $c->stash->{subject} =~ s/\r|\n/ /g;
+
+ my $base_url = $c->cobrand->base_url_for_emails( $c->cobrand->extra_data );
+ my $admin_base_url = $c->cobrand->admin_base_url
+ || 'https://secure.mysociety.org/admin/bci/';
+
+ if ( $c->stash->{update} ) {
+
+ my $problem_url = $base_url . '/report/' . $c->stash->{update}->problem_id
+ . '#update_' . $c->stash->{update}->id;
+ my $admin_url = $admin_base_url . '?page=update_edit;id=' . $c->stash->{update}->id;
+ $c->stash->{message} .= sprintf(
+ " \n\n[ Complaint about update %d on report %d - %s - %s ]",
+ $c->stash->{update}->id,
+ $c->stash->{update}->problem_id,
+ $problem_url, $admin_url
+ );
+ }
+ elsif ( $c->stash->{problem} ) {
+
+ my $problem_url = $base_url . '/report/' . $c->stash->{problem}->id;
+ my $admin_url = $admin_base_url . '?page=report_edit;id=' . $c->stash->{problem}->id;
+ $c->stash->{message} .= sprintf(
+ " \n\n[ Complaint about report %d - %s - %s ]",
+ $c->stash->{problem}->id,
+ $problem_url, $admin_url
+ );
+ }
+
+ return 1;
+}
+
+=head2 setup_request
+
+Pulls things from request into stash and adds other information
+generally required to stash
+
+=cut
+
+sub setup_request : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{contact_email} = $c->cobrand->contact_email;
+ $c->stash->{contact_email} =~ s/\@/&#64;/;
+
+ for my $param (qw/em subject message/) {
+ $c->stash->{$param} = $c->req->param($param);
+ }
+
+ # name is already used in the stash for the app class name
+ $c->stash->{form_name} = $c->req->param('name');
+
+ return 1;
+}
+
+=head2 send_email
+
+Sends the email
+
+=cut
+
+sub send_email : Private {
+ my ( $self, $c ) = @_;
+
+ my $recipient = $c->cobrand->contact_email();
+ my $recipient_name = $c->cobrand->contact_name();
+
+ $c->stash->{host} = $c->req->header('HOST');
+ $c->stash->{ip} = $c->req->address;
+ $c->stash->{ip} .=
+ $c->req->header('X-Forwarded-For')
+ ? ' ( forwarded from ' . $c->req->header('X-Forwarded-For') . ' )'
+ : '';
+
+ $c->send_email( 'contact.txt', {
+ to => [ [ $recipient, _($recipient_name) ] ],
+ from => [ $c->stash->{em}, $c->stash->{form_name} ],
+ subject => 'FMS message: ' . $c->stash->{subject},
+ });
+
+ # above is always succesful :(
+ $c->stash->{success} = 1;
+
+ return 1;
+}
+
+=head1 AUTHOR
+
+Struan Donald
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Council.pm b/perllib/FixMyStreet/App/Controller/Council.pm
new file mode 100644
index 000000000..35e3d0d11
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Council.pm
@@ -0,0 +1,107 @@
+package FixMyStreet::App::Controller::Council;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Council - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=head2 load_and_check_councils_and_wards
+
+Try to load councils and wards for this location and check that we have at least one. If
+there are no councils then return false.
+
+=cut
+
+sub load_and_check_councils_and_wards : Private {
+ my ( $self, $c ) = @_;
+ my @area_types = ( $c->cobrand->area_types(), @$mySociety::VotingArea::council_child_types );
+ $c->stash->{area_types} = \@area_types;
+ $c->forward('load_and_check_councils');
+}
+
+=head2 load_and_check_councils
+
+Try to load councils for this location and check that we have at least one. If
+there are no councils then return false.
+
+=cut
+
+sub load_and_check_councils : Private {
+ my ( $self, $c ) = @_;
+ my $latitude = $c->stash->{latitude};
+ my $longitude = $c->stash->{longitude};
+
+ # Look up councils and do checks for the point we've got
+ my @area_types;
+ if ( $c->stash->{area_types} and scalar @{ $c->stash->{area_types} } ) {
+ @area_types = @{ $c->stash->{area_types} };
+ } else {
+ @area_types = $c->cobrand->area_types();
+ }
+
+ # TODO: I think we want in_gb_locale around the MaPit line, needs testing
+ my $all_councils;
+ if ( $c->stash->{fetch_all_areas} ) {
+ my %area_types = map { $_ => 1 } @area_types;
+ my $all_areas =
+ mySociety::MaPit::call( 'point', "4326/$longitude,$latitude" );
+ $c->stash->{all_areas} = $all_areas;
+ $all_councils = {
+ map { $_ => $all_areas->{$_} }
+ grep { $area_types{ $all_areas->{$_}->{type} } }
+ keys %$all_areas
+ };
+ } else {
+ $all_councils =
+ mySociety::MaPit::call( 'point', "4326/$longitude,$latitude",
+ type => \@area_types );
+ }
+
+ # Let cobrand do a check
+ my ( $success, $error_msg ) =
+ $c->cobrand->council_check( { all_councils => $all_councils },
+ $c->stash->{council_check_action} );
+ if ( !$success ) {
+ $c->stash->{location_error} = $error_msg;
+ return;
+ }
+
+ # edit hash in-place
+ $c->cobrand->remove_redundant_councils($all_councils) if $c->stash->{remove_redundant_councils};
+
+ # If we don't have any councils we can't accept the report
+ if ( !scalar keys %$all_councils ) {
+ $c->stash->{location_offshore} = 1;
+ return;
+ }
+
+ # all good if we have some councils left
+ $c->stash->{all_councils} = $all_councils;
+ $c->stash->{all_council_names} =
+ [ map { $_->{name} } values %$all_councils ];
+ return 1;
+}
+
+=head1 AUTHOR
+
+Struan Donald
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/JSON.pm b/perllib/FixMyStreet/App/Controller/JSON.pm
new file mode 100644
index 000000000..3a3010911
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/JSON.pm
@@ -0,0 +1,136 @@
+package FixMyStreet::App::Controller::JSON;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+use JSON;
+use DateTime;
+use DateTime::Format::ISO8601;
+use List::MoreUtils 'uniq';
+
+=head1 NAME
+
+FixMyStreet::App::Controller::JSON - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Provide information as JSON
+
+=head1 METHODS
+
+=head2 problems
+
+Provide JSON of new/fixed problems in a specified time range
+
+=cut
+
+sub problems : Local {
+ my ( $self, $c, $path_type ) = @_;
+
+ # get the type from the path - this is to deal with the historic url
+ # structure. In futur
+ $path_type ||= '';
+ my $type =
+ $path_type eq 'new' ? 'new_problems'
+ : $path_type eq 'fixed' ? 'fixed_problems'
+ : '';
+
+ # gather the parameters
+ my $start_date = $c->req->param('start_date') || '';
+ my $end_date = $c->req->param('end_date') || '';
+
+ my $yyyy_mm_dd = qr{^\d{4}-\d\d-\d\d$};
+ if ( $start_date !~ $yyyy_mm_dd
+ || $end_date !~ $yyyy_mm_dd )
+ {
+ $c->stash->{error} = 'Invalid dates supplied';
+ return;
+ }
+
+ # convert the dates to datetimes and trap errors
+ my $iso8601 = DateTime::Format::ISO8601->new;
+ my $start_dt = eval { $iso8601->parse_datetime($start_date); };
+ my $end_dt = eval { $iso8601->parse_datetime($end_date); };
+ unless ( $start_dt && $end_dt ) {
+ $c->stash->{error} = 'Invalid dates supplied';
+ return;
+ }
+
+ # check that the dates are sane
+ if ( $start_dt > $end_dt ) {
+ $c->stash->{error} = 'Start date after end date';
+ return;
+ }
+
+ # check that the type is supported
+ unless ( $type eq 'new_problems' || $type eq 'fixed_problems' ) {
+ $c->stash->{error} = 'Invalid type supplied';
+ return;
+ }
+
+ # query the database
+ my ( $state, $date_col );
+ if ( $type eq 'new_problems' ) {
+ $state = 'confirmed';
+ $date_col = 'created';
+ } elsif ( $type eq 'fixed_problems' ) {
+ $state = 'fixed';
+ $date_col = 'lastupdate';
+ }
+
+ my $one_day = DateTime::Duration->new( days => 1 );
+ my @problems = $c->cobrand->problems->search( {
+ $date_col => {
+ '>=' => $start_dt,
+ '<=' => $end_dt + $one_day,
+ },
+ state => $state,
+ }, {
+ order_by => { -asc => 'confirmed' },
+ columns => [
+ 'id', 'title', 'council', 'category',
+ 'detail', 'name', 'anonymous', 'confirmed',
+ 'whensent', 'service',
+ ]
+ } );
+
+ my @councils;
+ foreach my $problem (@problems) {
+ $problem->name( '' ) if $problem->anonymous == 1;
+ $problem->service( 'Web interface' ) if $problem->service eq '';
+ if ($problem->council) {
+ (my $council = $problem->council) =~ s/\|.*//g;
+ my @council_ids = split /,/, $council;
+ push(@councils, @council_ids);
+ $problem->council( \@council_ids );
+ }
+ }
+ @councils = uniq @councils;
+ my $areas_info = mySociety::MaPit::call('areas', \@councils);
+ foreach my $problem (@problems) {
+ if ($problem->council) {
+ my @council_names = map { $areas_info->{$_}->{name} } @{$problem->council} ;
+ $problem->council( join(' and ', @council_names) );
+ }
+ }
+
+ @problems = map { { $_->get_columns } } @problems;
+ $c->stash->{response} = \@problems;
+}
+
+sub end : Private {
+ my ( $self, $c ) = @_;
+
+ my $response =
+ $c->stash->{error}
+ ? { error => $c->stash->{error} }
+ : $c->stash->{response};
+
+ $c->res->content_type('application/json; charset=utf-8');
+ $c->res->body( encode_json( $response || {} ) );
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Location.pm b/perllib/FixMyStreet/App/Controller/Location.pm
new file mode 100644
index 000000000..c1b119074
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Location.pm
@@ -0,0 +1,113 @@
+package FixMyStreet::App::Controller::Location;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+use Encode;
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Location - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+This is purely an internal controller for keeping all the location finding things in one place
+
+=head1 METHODS
+
+=head2 determine_location_from_coords
+
+Use latitude and longitude if provided in parameters.
+
+=cut
+
+sub determine_location_from_coords : Private {
+ my ( $self, $c ) = @_;
+
+ my $latitude = $c->req->param('latitude') || $c->req->param('lat');
+ my $longitude = $c->req->param('longitude') || $c->req->param('lon');
+
+ if ( defined $latitude && defined $longitude ) {
+ $c->stash->{latitude} = $latitude;
+ $c->stash->{longitude} = $longitude;
+
+ # Also save the pc if there is one
+ if ( my $pc = $c->req->param('pc') ) {
+ $c->stash->{pc} = $pc;
+ }
+
+ return 1;
+ }
+
+ return;
+}
+
+=head2 determine_location_from_pc
+
+User has searched for a location - try to find it for them.
+
+If one match is found returns true and lat/lng is set.
+
+If several possible matches are found puts an array onto stash so that user can be prompted to pick one and returns false.
+
+If no matches are found returns false.
+
+=cut
+
+sub determine_location_from_pc : Private {
+ my ( $self, $c, $pc ) = @_;
+
+ # check for something to search
+ $pc ||= $c->req->param('pc') || return;
+ $c->stash->{pc} = $pc; # for template
+
+ my ( $latitude, $longitude, $error ) =
+ eval { FixMyStreet::Geocode::lookup( $pc, $c ) };
+
+ # Check that nothing blew up
+ if ($@) {
+ warn "Error: $@";
+ return;
+ }
+
+ # If we got a lat/lng set to stash and return true
+ if ( defined $latitude && defined $longitude ) {
+ $c->stash->{latitude} = $latitude;
+ $c->stash->{longitude} = $longitude;
+ return 1;
+ }
+
+ # $error doubles up to return multiple choices by being an array
+ if ( ref($error) eq 'ARRAY' ) {
+ @$error = map {
+ decode_utf8($_);
+ s/, United Kingdom//;
+ s/, UK//;
+ $_;
+ } @$error;
+ $c->stash->{possible_location_matches} = $error;
+ return;
+ }
+
+ # pass errors back to the template
+ $c->stash->{location_error} = $error;
+ return;
+}
+
+=head1 AUTHOR
+
+Struan Donald
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/My.pm b/perllib/FixMyStreet/App/Controller/My.pm
new file mode 100644
index 000000000..1189fe901
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/My.pm
@@ -0,0 +1,36 @@
+package FixMyStreet::App::Controller::My;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+FixMyStreet::App::Controller::My - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+=head2 index
+
+=cut
+
+sub my : Path : Args(0) {
+ my ( $self, $c ) = @_;
+
+ # FIXME - handle not being logged in more elegantly
+ unless ( $c->user ) {
+ $c->res->redirect( $c->uri_for('/auth') );
+ $c->detach;
+ }
+
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Photo.pm b/perllib/FixMyStreet/App/Controller/Photo.pm
new file mode 100644
index 000000000..3c6255f3a
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Photo.pm
@@ -0,0 +1,93 @@
+package FixMyStreet::App::Controller::Photo;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+use DateTime::Format::HTTP;
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Photo - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+Display a photo
+
+=cut
+
+sub index :Path :Args(0) {
+ my ( $self, $c ) = @_;
+
+ my $id = $c->req->param('id');
+ my $comment = $c->req->param('c');
+ return unless ( $id || $comment );
+
+ my @photo;
+ if ( $comment ) {
+ @photo = $c->model('DB::Comment')->search( {
+ id => $comment,
+ state => 'confirmed',
+ photo => { '!=', undef },
+ } );
+ } else {
+ @photo = $c->cobrand->problems->search( {
+ id => $id,
+ state => [ 'confirmed', 'fixed', 'partial' ],
+ photo => { '!=', undef },
+ } );
+ }
+
+ $c->detach( '/page_error_404_not_found', [ 'No photo' ] )
+ unless @photo;
+
+ my $photo = $photo[0]->photo;
+ if ( $c->req->param('tn' ) ) {
+ $photo = _resize( $photo, 'x100' );
+ } elsif ( $c->cobrand->default_photo_resize ) {
+ $photo = _resize( $photo, $c->cobrand->default_photo_resize );
+ }
+
+ my $dt = DateTime->now();
+ $dt->set_year( $dt->year + 1 );
+
+ $c->res->content_type( 'image/jpeg' );
+ $c->res->header( 'expires', DateTime::Format::HTTP->format_datetime( $dt ) );
+ $c->res->body( $photo );
+}
+
+sub _resize {
+ my ($photo, $size) = @_;
+ use Image::Magick;
+ my $image = Image::Magick->new;
+ $image->BlobToImage($photo);
+ my $err = $image->Scale(geometry => "$size>");
+ throw Error::Simple("resize failed: $err") if "$err";
+ my @blobs = $image->ImageToBlob();
+ undef $image;
+ return $blobs[0];
+}
+
+=head1 AUTHOR
+
+Struan Donald
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Questionnaire.pm b/perllib/FixMyStreet/App/Controller/Questionnaire.pm
new file mode 100755
index 000000000..b4c9ade92
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Questionnaire.pm
@@ -0,0 +1,328 @@
+package FixMyStreet::App::Controller::Questionnaire;
+
+use Moose;
+use namespace::autoclean;
+use Utils;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Questionnaire - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Deals with report questionnaires.
+
+=head1 METHODS
+
+=cut
+
+=head2 load_questionnaire
+
+Loads the questionnaire from the database, and checks it still needs answering
+and is in the right state. Also finds out if this user has answered the
+"ever reported" question before.
+
+=cut
+
+sub load_questionnaire : Private {
+ my ( $self, $c ) = @_;
+
+ my $questionnaire = $c->model('DB::Questionnaire')->find(
+ { id => $c->stash->{id} },
+ { prefetch => 'problem' }
+ );
+ $c->stash->{questionnaire} = $questionnaire;
+
+ my $problem_id = $questionnaire->problem_id;
+
+ if ( $questionnaire->whenanswered ) {
+ my $problem_url = $c->uri_for( "/report/$problem_id" );
+ my $contact_url = $c->uri_for( "/contact" );
+ $c->stash->{message} = sprintf(_("You have already answered this questionnaire. If you have a question, please <a href='%s'>get in touch</a>, or <a href='%s'>view your problem</a>.\n"), $contact_url, $problem_url);
+ $c->stash->{template} = 'questionnaire/error.html';
+ $c->detach;
+ }
+
+ unless ( $questionnaire->problem->state eq 'confirmed' || $questionnaire->problem->state eq 'fixed' ) {
+ $c->detach('missing_problem');
+ }
+
+ $c->stash->{problem} = $questionnaire->problem;
+ $c->stash->{answered_ever_reported} = $questionnaire->problem->user->answered_ever_reported;
+
+ # EHA needs to know how many to alter display, and whether to send another or not
+ if ($c->cobrand->moniker eq 'emptyhomes') {
+ $c->stash->{num_questionnaire} = $c->model('DB::Questionnaire')->count(
+ { problem_id => $c->stash->{problem}->id }
+ );
+ }
+
+}
+
+=head2 submit
+
+If someone submits a questionnaire - either a full style one (when we'll have a
+token), or the mini own-report one (when we'll have a problem ID).
+
+=cut
+
+sub submit : Path('submit') {
+ my ( $self, $c ) = @_;
+
+ if ( $c->req->params->{token} ) {
+ $c->forward('submit_standard');
+ } elsif ( $c->req->params->{problem} ) {
+ $c->forward('submit_creator_fixed');
+ } else {
+ return;
+ }
+
+ return 1;
+}
+
+=head2 missing_problem
+
+Display couldn't locate problem error message
+
+=cut
+
+sub missing_problem : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{message} = _("I'm afraid we couldn't locate your problem in the database.\n");
+ $c->stash->{template} = 'questionnaire/error.html';
+}
+
+sub submit_creator_fixed : Private {
+ my ( $self, $c ) = @_;
+
+ my @errors;
+
+ map { $c->stash->{$_} = $c->req->params->{$_} || '' } qw(reported problem);
+
+ # should only be able to get to here if we are logged and we have a
+ # problem
+ unless ( $c->user && $c->stash->{problem} ) {
+ $c->detach('missing_problem');
+ }
+
+ my $problem = $c->cobrand->problems->find( { id => $c->stash->{problem} } );
+
+ # you should not be able to answer questionnaires about problems
+ # that you've not submitted
+ if ( $c->user->id != $problem->user->id ) {
+ $c->detach('missing_problem');
+ }
+
+ push @errors, _('Please say whether you\'ve ever reported a problem to your council before') unless $c->stash->{reported};
+
+ $c->stash->{problem_id} = $c->stash->{problem};
+ $c->stash->{errors} = \@errors;
+ $c->detach( 'creator_fixed' ) if scalar @errors;
+
+ my $questionnaire = $c->model( 'DB::Questionnaire' )->find_or_new(
+ {
+ problem_id => $c->stash->{problem},
+ old_state => 'confirmed',
+ new_state => 'fixed',
+ }
+ );
+
+ unless ( $questionnaire->in_storage ) {
+ $questionnaire->ever_reported( $c->stash->{reported} eq 'Yes' ? 1 : 0 );
+ $questionnaire->whensent( \'ms_current_timestamp()' );
+ $questionnaire->whenanswered( \'ms_current_timestamp()' );
+ $questionnaire->insert;
+ }
+
+ $c->stash->{creator_fixed} = 1;
+ $c->stash->{template} = 'tokens/confirm_update.html';
+
+ return 1;
+}
+
+sub submit_standard : Private {
+ my ( $self, $c ) = @_;
+
+ $c->forward( '/tokens/load_questionnaire_id', [ $c->req->params->{token} ] );
+ $c->forward( 'load_questionnaire' );
+ $c->forward( 'process_questionnaire' );
+
+ my $problem = $c->stash->{problem};
+ my $old_state = $problem->state;
+ my $new_state = '';
+ $new_state = 'fixed' if $c->stash->{been_fixed} eq 'Yes' && $old_state eq 'confirmed';
+ $new_state = 'confirmed' if $c->stash->{been_fixed} eq 'No' && $old_state eq 'fixed';
+
+ # Record state change, if there was one
+ if ( $new_state ) {
+ $problem->state( $new_state );
+ $problem->lastupdate( \'ms_current_timestamp()' );
+ }
+
+ # If it's not fixed and they say it's still not been fixed, record time update
+ if ( $c->stash->{been_fixed} eq 'No' && $old_state eq 'confirmed' ) {
+ $problem->lastupdate( \'ms_current_timestamp()' );
+ }
+
+ # Record questionnaire response
+ my $reported = undef;
+ $reported = 1 if $c->stash->{reported} eq 'Yes';
+ $reported = 0 if $c->stash->{reported} eq 'No';
+
+ my $q = $c->stash->{questionnaire};
+ $q->update( {
+ whenanswered => \'ms_current_timestamp()',
+ ever_reported => $reported,
+ old_state => $old_state,
+ new_state => $c->stash->{been_fixed} eq 'Unknown' ? 'unknown' : ($new_state || $old_state),
+ } );
+
+ # Record an update if they've given one, or if there's a state change
+ if ( $new_state || $c->stash->{update} ) {
+ my $update = $c->stash->{update} || _('Questionnaire filled in by problem reporter');
+ $update = $c->model('DB::Comment')->new(
+ {
+ problem => $problem,
+ name => $problem->name,
+ user => $problem->user,
+ text => $update,
+ state => 'confirmed',
+ mark_fixed => $new_state eq 'fixed' ? 1 : 0,
+ mark_open => $new_state eq 'confirmed' ? 1 : 0,
+ lang => $c->stash->{lang_code},
+ cobrand => $c->cobrand->moniker,
+ cobrand_data => $c->cobrand->extra_update_data,
+ confirmed => \'ms_current_timestamp()',
+ anonymous => $problem->anonymous,
+ }
+ );
+ if ( my $fileid = $c->stash->{upload_fileid} ) {
+ my $file = file( $c->config->{UPLOAD_CACHE}, "$fileid.jpg" );
+ my $blob = $file->slurp;
+ $file->remove;
+ $update->photo($blob);
+ }
+ $update->insert;
+ }
+
+ # If they've said they want another questionnaire, mark as such
+ $problem->send_questionnaire( 1 )
+ if ($c->stash->{been_fixed} eq 'No' || $c->stash->{been_fixed} eq 'Unknown') && $c->stash->{another} eq 'Yes';
+ $problem->update;
+
+ $c->stash->{new_state} = $new_state;
+ $c->stash->{template} = 'questionnaire/completed.html';
+}
+
+sub process_questionnaire : Private {
+ my ( $self, $c ) = @_;
+
+ map { $c->stash->{$_} = $c->req->params->{$_} || '' } qw(been_fixed reported another update);
+
+ # EHA questionnaires done for you
+ if ($c->cobrand->moniker eq 'emptyhomes') {
+ $c->stash->{another} = $c->stash->{num_questionnaire}==1 ? 'Yes' : 'No';
+ }
+
+ my @errors;
+ push @errors, _('Please state whether or not the problem has been fixed')
+ unless $c->stash->{been_fixed};
+
+ if ($c->cobrand->ask_ever_reported) {
+ push @errors, _('Please say whether you\'ve ever reported a problem to your council before')
+ unless $c->stash->{reported} || $c->stash->{answered_ever_reported};
+ }
+
+ push @errors, _('Please indicate whether you\'d like to receive another questionnaire')
+ if ($c->stash->{been_fixed} eq 'No' || $c->stash->{been_fixed} eq 'Unknown') && !$c->stash->{another};
+
+ push @errors, _('Please provide some explanation as to why you\'re reopening this report')
+ if $c->stash->{been_fixed} eq 'No' && $c->stash->{problem}->state eq 'fixed' && !$c->stash->{update};
+
+ $c->forward('/report/new/process_photo');
+ push @errors, $c->stash->{photo_error}
+ if $c->stash->{photo_error};
+
+ push @errors, _('Please provide some text as well as a photo')
+ if $c->stash->{upload_fileid} && !$c->stash->{update};
+
+ if (@errors) {
+ $c->stash->{errors} = [ @errors ];
+ $c->detach( 'display' );
+ }
+}
+
+# Sent here from email token action. Simply load and display questionnaire.
+sub index : Private {
+ my ( $self, $c ) = @_;
+ $c->forward( 'load_questionnaire' );
+ $c->forward( 'display' );
+}
+
+=head2 display
+
+Displays a questionnaire, either after bad submission or directly from email token.
+
+=cut
+
+sub display : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{template} = 'questionnaire/index.html';
+
+ my $problem = $c->stash->{questionnaire}->problem;
+
+ ( $c->stash->{short_latitude}, $c->stash->{short_longitude} ) =
+ map { Utils::truncate_coordinate($_) }
+ ( $problem->latitude, $problem->longitude );
+
+ $c->stash->{updates} = $c->model('DB::Comment')->search(
+ { problem_id => $problem->id, state => 'confirmed' },
+ { order_by => 'confirmed' }
+ );
+
+ FixMyStreet::Map::display_map(
+ $c,
+ latitude => $problem->latitude,
+ longitude => $problem->longitude,
+ pins => [ {
+ latitude => $problem->latitude,
+ longitude => $problem->longitude,
+ colour => $problem->state eq 'fixed' ? 'green' : 'red',
+ } ],
+ );
+}
+
+=head2 creator_fixed
+
+Display the reduced questionnaire that we display when the reporter of a
+problem submits an update marking it as fixed.
+
+=cut
+
+sub creator_fixed : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{template} = 'questionnaire/creator_fixed.html';
+
+ return 1;
+}
+
+=head1 AUTHOR
+
+Matthew Somerville
+
+=head1 LICENSE
+
+Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved.
+Licensed under the Affero GPL.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;
+
diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm
new file mode 100644
index 000000000..497ec784a
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Report.pm
@@ -0,0 +1,160 @@
+package FixMyStreet::App::Controller::Report;
+
+use Moose;
+use namespace::autoclean;
+BEGIN { extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Report - display a report
+
+=head1 DESCRIPTION
+
+Show a report
+
+=head1 ACTIONS
+
+=head2 index
+
+Redirect to homepage unless C<id> parameter in query, in which case redirect to
+'/report/$id'.
+
+=cut
+
+sub index : Path('') : Args(0) {
+ my ( $self, $c ) = @_;
+
+ my $id = $c->req->param('id');
+
+ my $uri =
+ $id
+ ? $c->uri_for( '/report', $id )
+ : $c->uri_for('/');
+
+ $c->res->redirect($uri);
+}
+
+=head2 report_display
+
+Display a report.
+
+=cut
+
+sub display : Path('') : Args(1) {
+ my ( $self, $c, $id ) = @_;
+
+ if (
+ $id =~ m{ ^ 3D (\d+) $ }x # Some council with bad email software
+ || $id =~ m{ ^(\d+) \D .* $ }x # trailing garbage
+ )
+ {
+ return $c->res->redirect( $c->uri_for($1), 301 );
+ }
+
+ $c->forward('load_problem_or_display_error', [ $id ] );
+ $c->forward( 'load_updates' );
+ $c->forward( 'format_problem_for_display' );
+}
+
+sub load_problem_or_display_error : Private {
+ my ( $self, $c, $id ) = @_;
+
+ # try to load a report if the id is a number
+ my $problem #
+ = $id =~ m{\D} # is id non-numeric?
+ ? undef # ...don't even search
+ : $c->cobrand->problems->find( { id => $id } );
+
+ # check that the problem is suitable to show.
+ if ( !$problem || $problem->state eq 'unconfirmed' ) {
+ $c->detach( '/page_error_404_not_found', [ _('Unknown problem ID') ] );
+ }
+ elsif ( $problem->state eq 'hidden' ) {
+ $c->detach(
+ '/page_error_410_gone',
+ [ _('That report has been removed from FixMyStreet.') ] #
+ );
+ }
+
+ $c->stash->{problem} = $problem;
+ return 1;
+}
+
+sub load_updates : Private {
+ my ( $self, $c ) = @_;
+
+ my $updates = $c->model('DB::Comment')->search(
+ { problem_id => $c->stash->{problem}->id, state => 'confirmed' },
+ { order_by => 'confirmed' }
+ );
+
+ $c->stash->{updates} = $updates;
+
+ return 1;
+}
+
+sub format_problem_for_display : Private {
+ my ( $self, $c ) = @_;
+
+ my $problem = $c->stash->{problem};
+
+ $c->stash->{banner} = $c->cobrand->generate_problem_banner($problem);
+
+ $c->stash->{cobrand_alert_fields} = $c->cobrand->form_elements('/alerts');
+ $c->stash->{cobrand_update_fields} =
+ $c->cobrand->form_elements('/updateForm');
+
+ ( $c->stash->{short_latitude}, $c->stash->{short_longitude} ) =
+ map { Utils::truncate_coordinate($_) }
+ ( $problem->latitude, $problem->longitude );
+
+ $c->stash->{report_name} = $c->req->param('name');
+
+ if ( $c->req->param('submit_update') ) {
+ # we may have munged these previously in /report/update
+ # so only set if they're not already in the stash
+ $c->stash->{form_name} ||= $c->req->param('name');
+ $c->stash->{update_text} ||= $c->req->param('update');
+ $c->stash->{email} ||= $c->req->param('rznvy');
+ $c->stash->{fixed} ||= $c->req->param('fixed') ? ' checked' : '';
+ $c->stash->{add_alert_checked} ||=
+ ( $c->req->param('add_alert') ? ' checked' : '' );
+ }
+ else {
+ if ( $c->user ) {
+ $c->stash->{form_name} = $c->user->name;
+ $c->stash->{email} = $c->user->email;
+ $c->stash->{may_show_name} = ' checked' if $c->user->name;
+ }
+ $c->stash->{add_alert_checked} = ' checked';
+ }
+
+ $c->forward('generate_map_tags');
+
+ return 1;
+}
+
+sub generate_map_tags : Private {
+ my ( $self, $c ) = @_;
+
+ my $problem = $c->stash->{problem};
+
+ FixMyStreet::Map::display_map(
+ $c,
+ latitude => $problem->latitude,
+ longitude => $problem->longitude,
+ pins => $problem->used_map
+ ? [ {
+ latitude => $problem->latitude,
+ longitude => $problem->longitude,
+ colour => 'blue',
+ } ]
+ : [],
+ );
+
+ return 1;
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm
new file mode 100644
index 000000000..375b36a51
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Report/New.pm
@@ -0,0 +1,1020 @@
+package FixMyStreet::App::Controller::Report::New;
+
+use Moose;
+use namespace::autoclean;
+BEGIN { extends 'Catalyst::Controller'; }
+
+use FixMyStreet::Geocode;
+use Encode;
+use Image::Magick;
+use List::MoreUtils qw(uniq);
+use POSIX 'strcoll';
+use HTML::Entities;
+use mySociety::MaPit;
+use Path::Class;
+use Utils;
+use mySociety::EmailUtil;
+use mySociety::TempFiles;
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Report::New
+
+=head1 DESCRIPTION
+
+Create a new report, or complete a partial one .
+
+=head1 PARAMETERS
+
+=head2 flow control
+
+submit_map: true if we reached this page by clicking on the map
+
+submit_problem: true if a problem has been submitted
+
+=head2 location (required)
+
+We require a location - either lat/lng or a tile click.
+
+longitude, latitude: location of the report - either determined from the
+address/postcode or from a map click.
+
+x, y, tile_xxx.yyy.x, tile_xxx.yyy.y: x and y are the tile locations. The
+'tile_xxx.yyy' pair are the click locations on the tile. These can be converted
+back into lat/lng by the map code.
+
+=head2 image related
+
+Parameters are 'photo' or 'upload_fileid'. The 'photo' is used when a user has selected a file. Once it has been uploaded it is cached on disk so that if there are errors on the form it need not be uploaded again. The cache location is stored in 'upload_fileid'.
+
+=head2 optional
+
+pc: location user searched for
+
+skipped: true if the map was skipped - may mean that the location is not as
+accurate as we'd like. Default is false.
+
+upload_fileid: set if there is an uploaded file (might not be needed if we use the Catalyst upload handlers)
+
+may_show_name: bool - false if the user wants this report to be anonymous.
+
+title
+
+detail
+
+name
+
+email
+
+phone
+
+partial
+
+=head2 can be ignored
+
+all_pins: related to map display - not relevant to creation of a new report
+
+=cut
+
+sub report_new : Path : Args(0) {
+ my ( $self, $c ) = @_;
+
+ # create the report - loading a partial if available
+ $c->forward('initialize_report');
+
+ # work out the location for this report and do some checks
+ return $c->forward('redirect_to_around')
+ unless $c->forward('determine_location');
+
+ # create a problem from the submitted details
+ $c->stash->{template} = "report/new/fill_in_details.html";
+ $c->forward('setup_categories_and_councils');
+ $c->forward('generate_map');
+
+ # deal with the user and report and check both are happy
+ return
+ unless $c->forward('process_user')
+ && $c->forward('process_report')
+ && $c->forward('process_photo')
+ && $c->forward('check_form_submitted')
+ && $c->forward('check_for_errors')
+ && $c->forward('save_user_and_report')
+ && $c->forward('redirect_or_confirm_creation');
+}
+
+=head2 report_import
+
+Action to accept report creations from iPhones and other mobile apps. URL is
+'/import' to be compatible with existing apps.
+
+=cut
+
+sub report_import : Path('/import') {
+ my ( $self, $c ) = @_;
+
+ # If this is not a POST then just print out instructions for using page
+ return unless $c->req->method eq 'POST';
+
+ # anything else we return is plain text
+ $c->res->content_type('text/plain; charset=utf-8');
+
+ my %input =
+ map { $_ => $c->req->param($_) || '' } (
+ 'service', 'subject', 'detail', 'name', 'email', 'phone',
+ 'easting', 'northing', 'lat', 'lon', 'id', 'phone_id',
+ );
+
+ my @errors;
+
+ # Get our location
+ my $latitude = $input{lat} ||= 0;
+ my $longitude = $input{lon} ||= 0;
+ if (
+ !( $latitude || $longitude ) # have not been given lat or lon
+ && ( $input{easting} && $input{northing} ) # but do have e and n
+ )
+ {
+ ( $latitude, $longitude ) =
+ Utils::convert_en_to_latlon( $input{easting}, $input{northing} );
+ }
+
+ # handle the photo upload
+ $c->forward( 'process_photo_upload', [ { rotate_photo => 1 } ] );
+ my $photo = $c->stash->{upload_fileid};
+ if ( my $error = $c->stash->{photo_error} ) {
+ push @errors, $error;
+ }
+
+ push @errors, 'You must supply a service' unless $input{service};
+ push @errors, 'Please enter a subject' unless $input{subject} =~ /\S/;
+ push @errors, 'Please enter your name' unless $input{name} =~ /\S/;
+
+ if ( $input{email} !~ /\S/ ) {
+ push @errors, 'Please enter your email';
+ }
+ elsif ( !mySociety::EmailUtil::is_valid_email( $input{email} ) ) {
+ push @errors, 'Please enter a valid email';
+ }
+
+ if ( $latitude && $c->config->{COUNTRY} eq 'GB' ) {
+ eval { Utils::convert_latlon_to_en( $latitude, $longitude ); };
+ push @errors,
+ "We had a problem with the supplied co-ordinates - outside the UK?"
+ if $@;
+ }
+
+ unless ( $photo || ( $latitude || $longitude ) ) {
+ push @errors, 'Either a location or a photo must be provided.';
+ }
+
+ # if we have errors then we should bail out
+ if (@errors) {
+ my $body = join '', map { "ERROR:$_\n" } @errors;
+ $c->res->body($body);
+ return;
+ }
+
+### leaving commented out for now as the values stored here never appear to
+### get used and the new user accounts might make them redundant anyway.
+ #
+ # # Store for possible future use
+ # if ( $input{id} || $input{phone_id} ) {
+ # my $id = $input{id} || $input{phone_id};
+ # my $already =
+ # dbh()
+ # ->selectrow_array(
+ # 'select id from partial_user where service=? and nsid=?',
+ # {}, $input{service}, $id );
+ # unless ($already) {
+ # dbh()->do(
+ # 'insert into partial_user (service, nsid, name, email, phone)'
+ # . ' values (?, ?, ?, ?, ?)',
+ # {},
+ # $input{service},
+ # $id,
+ # $input{name},
+ # $input{email},
+ # $input{phone}
+ # );
+ # }
+ # }
+
+ # find or create the user
+ my $report_user = $c->model('DB::User')->find_or_create(
+ {
+ email => $input{email},
+ name => $input{name},
+ phone => $input{phone}
+ }
+ );
+
+ # create a new report (don't save it yet)
+ my $report = $c->model('DB::Problem')->new(
+ {
+ user => $report_user,
+ postcode => '',
+ latitude => $latitude,
+ longitude => $longitude,
+ title => $input{subject},
+ detail => $input{detail},
+ name => $input{name},
+ service => $input{service},
+ state => 'partial',
+ used_map => 1,
+ anonymous => 0,
+ category => '',
+ areas => '',
+
+ }
+ );
+
+ # If there was a photo add that too
+ if ( my $fileid = $c->stash->{upload_fileid} ) {
+ my $file = file( $c->config->{UPLOAD_CACHE}, "$fileid.jpg" );
+ my $blob = $file->slurp;
+ $file->remove;
+ $report->photo($blob);
+ }
+
+ # save the report;
+ $report->insert();
+
+ my $token =
+ $c->model("DB::Token")
+ ->create( { scope => 'partial', data => $report->id } );
+
+ $c->stash->{report} = $report;
+ $c->stash->{token_url} = $c->uri_for_email( '/L', $token->token );
+
+ $c->send_email( 'partial.txt', { to => $report->user->email, } );
+
+ $c->res->body('SUCCESS');
+ return 1;
+}
+
+=head2 initialize_report
+
+Create the report and set up some basics in it. If there is a partial report
+requested then use that .
+
+Partial reports are created when people submit to us e.g. via mobile apps.
+They are in the database but are not completed yet. Users reach us by following
+a link we email them that contains a token link. This action looks for the
+token and if found retrieves the report in it.
+
+=cut
+
+sub initialize_report : Private {
+ my ( $self, $c ) = @_;
+
+ # check to see if there is a partial report that we should use, otherwise
+ # create a new one. Stick it on the stash.
+ my $report = undef;
+
+ if ( my $partial = scalar $c->req->param('partial') ) {
+
+ for (1) { # use as pseudo flow control
+
+ # did we find a token
+ last unless $partial;
+
+ # is it in the database
+ my $token =
+ $c->model("DB::Token")
+ ->find( { scope => 'partial', token => $partial } ) #
+ || last;
+
+ # can we get an id from it?
+ my $id = $token->data #
+ || last;
+
+ # load the related problem
+ $report = $c->cobrand->problems #
+ ->search( { id => $id, state => 'partial' } ) #
+ ->first;
+
+ if ($report) {
+
+ # log the problem creation user in to the site
+ $c->authenticate( { email => $report->user->email },
+ 'no_password' );
+
+ # save the token to delete at the end
+ $c->stash->{partial_token} = $token if $report;
+
+ }
+ else {
+
+ # no point keeping it if it is done.
+ $token->delete;
+ }
+ }
+ }
+
+ if ( !$report ) {
+
+ # If we didn't find a partial then create a new one
+ $report = $c->model('DB::Problem')->new( {} );
+
+ # If we have a user logged in let's prefill some values for them.
+ if ( $c->user ) {
+ my $user = $c->user->obj;
+ $report->user($user);
+ $report->name( $user->name );
+ }
+
+ }
+
+ $c->stash->{report} = $report;
+
+ return 1;
+}
+
+=head2 determine_location
+
+Work out what the location of the report should be - either by using lat,lng or
+a tile click or what's come in from a partial. Returns false if no location
+could be found.
+
+=cut
+
+sub determine_location : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{fetch_all_areas} = 1;
+ return 1
+ if #
+ ( #
+ $c->forward('determine_location_from_tile_click')
+ || $c->forward('/location/determine_location_from_coords')
+ || $c->forward('determine_location_from_report')
+ ) #
+ && $c->forward('/around/check_location_is_acceptable');
+ return;
+}
+
+=head2 determine_location_from_tile_click
+
+Detect that the map tiles have been clicked on by looking for the tile
+parameters.
+
+=cut
+
+sub determine_location_from_tile_click : Private {
+ my ( $self, $c ) = @_;
+
+ # example: 'tile_1673.1451.x'
+ my $param_key_regex = '^tile_(\d+)\.(\d+)\.[xy]$';
+
+ my @matching_param_keys =
+ grep { m/$param_key_regex/ } keys %{ $c->req->params };
+
+ # did we find any matches
+ return unless scalar(@matching_param_keys) == 2;
+
+ # get the x and y keys
+ my ( $x_key, $y_key ) = sort @matching_param_keys;
+
+ # Extract the data needed
+ my ( $pin_tile_x, $pin_tile_y ) = $x_key =~ m{$param_key_regex};
+ my $pin_x = $c->req->param($x_key);
+ my $pin_y = $c->req->param($y_key);
+
+ # return if they are both 0 - this happens when you submit the form by
+ # hitting enter and not using the button. It also happens if you click
+ # exactly there on the map but that is less likely than hitting return to
+ # submit. Lesser of two evils...
+ return unless $pin_x && $pin_y;
+
+ # convert the click to lat and lng
+ my ( $latitude, $longitude ) = FixMyStreet::Map::click_to_wgs84(
+ $c,
+ $pin_tile_x, $pin_x, $pin_tile_y, $pin_y
+ );
+
+ # store it on the stash
+ $c->stash->{latitude} = $latitude;
+ $c->stash->{longitude} = $longitude;
+
+ # set a flag so that the form is not considered submitted. This will prevent
+ # errors showing on the fields.
+ $c->stash->{force_form_not_submitted} = 1;
+
+ # return true as we found a location
+ return 1;
+}
+
+=head2 determine_location_from_report
+
+Use latitude and longitude stored in the report - this is probably result of a
+partial report being loaded.
+
+=cut
+
+sub determine_location_from_report : Private {
+ my ( $self, $c ) = @_;
+
+ my $report = $c->stash->{report};
+
+ if ( defined $report->latitude && defined $report->longitude ) {
+ $c->stash->{latitude} = $report->latitude;
+ $c->stash->{longitude} = $report->longitude;
+ return 1;
+ }
+
+ return;
+}
+
+=head2 setup_categories_and_councils
+
+Look up categories for this council or councils
+
+=cut
+
+sub setup_categories_and_councils : Private {
+ my ( $self, $c ) = @_;
+
+ my $all_councils = $c->stash->{all_councils};
+ my $first_council = ( values %$all_councils )[0];
+
+ my @contacts #
+ = $c #
+ ->model('DB::Contact') #
+ ->not_deleted #
+ ->search( { area_id => [ keys %$all_councils ] } ) #
+ ->all;
+
+ # variables to populate
+ my %area_ids_to_list = (); # Areas with categories assigned
+ my @category_options = (); # categories to show
+ my $category_label = undef; # what to call them
+
+ # FIXME - implement in cobrand
+ if ( $c->cobrand->moniker eq 'emptyhomes' ) {
+
+ # add all areas found to the list
+ foreach (@contacts) {
+ $area_ids_to_list{ $_->area_id } = 1;
+ }
+
+ # set our own categories
+ @category_options = (
+ _('-- Pick a property type --'),
+ _('Empty house or bungalow'),
+ _('Empty flat or maisonette'),
+ _('Whole block of empty flats'),
+ _('Empty office or other commercial'),
+ _('Empty pub or bar'),
+ _('Empty public building - school, hospital, etc.')
+ );
+ $category_label = _('Property type:');
+
+ } elsif ($first_council->{type} eq 'LBO') {
+
+ $area_ids_to_list{ $first_council->{id} } = 1;
+ @category_options = (
+ _('-- Pick a category --'),
+ sort keys %{ Utils::london_categories() }
+ );
+ $category_label = _('Category:');
+
+ } else {
+
+ # keysort does not appear to obey locale so use strcoll (see i18n.t)
+ @contacts = sort { strcoll( $a->category, $b->category ) } @contacts;
+
+ my %seen;
+ foreach my $contact (@contacts) {
+
+ $area_ids_to_list{ $contact->area_id } = 1;
+
+ next # TODO - move this to the cobrand
+ if $c->cobrand->moniker eq 'southampton'
+ && $contact->category eq 'Street lighting';
+
+ next if $contact->category eq _('Other');
+
+ push @category_options, $contact->category
+ unless $seen{$contact->category};
+ $seen{$contact->category} = 1;
+ }
+
+ if (@category_options) {
+ @category_options =
+ ( _('-- Pick a category --'), @category_options, _('Other') );
+ $category_label = _('Category:');
+ }
+ }
+
+ # put results onto stash for display
+ $c->stash->{area_ids_to_list} = [ keys %area_ids_to_list ];
+ $c->stash->{category_label} = $category_label;
+ $c->stash->{category_options} = \@category_options;
+
+ my @missing_details_councils =
+ grep { !$area_ids_to_list{$_} } #
+ keys %$all_councils;
+
+ my @missing_details_council_names =
+ map { $all_councils->{$_}->{name} } #
+ @missing_details_councils;
+
+ $c->stash->{missing_details_councils} = \@missing_details_councils;
+ $c->stash->{missing_details_council_names} = \@missing_details_council_names;
+}
+
+=head2 check_form_submitted
+
+ $bool = $c->forward('check_form_submitted');
+
+Returns true if the form has been submitted, false if not. Determines this based
+on the presence of the C<submit_problem> parameter.
+
+=cut
+
+sub check_form_submitted : Private {
+ my ( $self, $c ) = @_;
+ return if $c->stash->{force_form_not_submitted};
+ return $c->req->param('submit_problem') || '';
+}
+
+=head2 process_user
+
+Load user from the database or prepare a new one.
+
+=cut
+
+sub process_user : Private {
+ my ( $self, $c ) = @_;
+
+ # FIXME - If user already logged in use them regardless
+
+ # Extract all the params to a hash to make them easier to work with
+ my %params = #
+ map { $_ => scalar $c->req->param($_) } #
+ ( 'email', 'name', 'phone', );
+
+ # cleanup the email address
+ my $email = $params{email} ? lc $params{email} : '';
+ $email =~ s{\s+}{}g;
+
+ my $report = $c->stash->{report};
+ my $report_user #
+ = ( $report ? $report->user : undef )
+ || $c->model('DB::User')->find_or_new( { email => $email } );
+
+ # set the user's name and phone (if given)
+ $report_user->name( Utils::trim_text( $params{name} ) );
+ $report_user->phone( Utils::trim_text( $params{phone} ) ) if $params{phone};
+
+ $c->stash->{report_user} = $report_user;
+
+ return 1;
+}
+
+=head2 process_report
+
+Looking at the parameters passed in create a new item and return it. Does not
+save anything to the database. If no item can be created (ie no information
+provided) returns undef.
+
+=cut
+
+sub process_report : Private {
+ my ( $self, $c ) = @_;
+
+ # Extract all the params to a hash to make them easier to work with
+ my %params = #
+ map { $_ => scalar $c->req->param($_) } #
+ (
+ 'title', 'detail', 'pc', #
+ 'name', 'may_show_name', #
+ 'category', #
+ 'partial', 'skipped', 'submit_problem' #
+ );
+
+ # load the report
+ my $report = $c->stash->{report};
+
+ # Enter the location and other bits which are not from the form
+ $report->postcode( $params{pc} );
+ $report->latitude( $c->stash->{latitude} );
+ $report->longitude( $c->stash->{longitude} );
+
+ # Capture whether the map was used
+ $report->used_map( $params{skipped} ? 0 : 1 );
+
+ # Short circuit unless the form has been submitted
+ return 1 unless $params{submit_problem};
+
+ # set some simple bool values (note they get inverted)
+ $report->anonymous( $params{may_show_name} ? 0 : 1 );
+
+ # clean up text before setting
+ $report->title( Utils::cleanup_text( $params{title} ) );
+ $report->detail(
+ Utils::cleanup_text( $params{detail}, { allow_multiline => 1 } ) );
+
+ # set these straight from the params
+ $report->name( Utils::trim_text( $params{name} ) );
+ $report->category( _ $params{category} );
+
+ my $areas = $c->stash->{all_areas};
+ $report->areas( ',' . join( ',', sort keys %$areas ) . ',' );
+
+ # From earlier in the process.
+ my $councils = $c->stash->{all_councils};
+ my $first_council = ( values %$councils )[0];
+
+ if ( $c->cobrand->moniker eq 'emptyhomes' ) {
+
+ # all councils have all categories for emptyhomes
+ $report->council( join( ',', keys %$councils) );
+
+ } elsif ( $first_council->{type} eq 'LBO') {
+
+ unless ( Utils::london_categories()->{ $report->category } ) {
+ # TODO Perfect world, this wouldn't short-circuit, other errors would
+ # be included as well.
+ $c->stash->{field_errors} = { category => _('Please choose a category') };
+ return;
+ }
+ $report->council( $first_council->{id} );
+
+ } elsif ( $report->category ) {
+
+ # FIXME All contacts were fetched in setup_categories_and_councils,
+ # so can this DB call also be avoided?
+ my @contacts = $c-> #
+ model('DB::Contact') #
+ ->not_deleted #
+ ->search(
+ {
+ area_id => [ keys %$councils ],
+ category => $report->category
+ }
+ )->all;
+
+ unless ( @contacts ) {
+ $c->stash->{field_errors} = { category => _('Please choose a category') };
+ return;
+ }
+
+ # construct the council string:
+ # 'x,x' - x are council IDs that have this category
+ # 'x,x|y,y' - x are council IDs that have this category, y council IDs with *no* contact
+ my $council_string = join( ',', map { $_->area_id } @contacts );
+ $council_string .=
+ '|' . join( ',', @{ $c->stash->{missing_details_councils} } )
+ if $council_string && @{ $c->stash->{missing_details_councils} };
+ $report->council($council_string);
+
+ } elsif ( @{ $c->stash->{area_ids_to_list} } ) {
+
+ # There was an area with categories, but we've not been given one. Bail.
+ $c->stash->{field_errors} = { category => _('Please choose a category') };
+ return;
+
+ } else {
+
+ # If we're here, we've been submitted somewhere
+ # where we have no contact information at all.
+ $report->council( -1 );
+
+ }
+
+ # set defaults that make sense
+ $report->state('unconfirmed');
+
+ # save the cobrand and language related information
+ $report->cobrand( $c->cobrand->moniker );
+ $report->cobrand_data( $c->cobrand->extra_problem_data );
+ $report->lang( $c->stash->{lang_code} );
+
+ return 1;
+}
+
+=head2 process_photo
+
+Handle the photo - either checking and storing it after an upload or retrieving
+it from the cache.
+
+Store any error message onto 'photo_error' in stash.
+=cut
+
+sub process_photo : Private {
+ my ( $self, $c ) = @_;
+
+ return
+ $c->forward('process_photo_upload')
+ || $c->forward('process_photo_cache')
+ || 1; # always return true
+}
+
+sub process_photo_upload : Private {
+ my ( $self, $c, $args ) = @_;
+
+ # setup args and set defaults
+ $args ||= {};
+ $args->{rotate_photo} ||= 0;
+
+ # check for upload or return
+ my $upload = $c->req->upload('photo')
+ || return;
+
+ # check that the photo is a jpeg
+ my $ct = $upload->type;
+ unless ( $ct eq 'image/jpeg' || $ct eq 'image/pjpeg' ) {
+ $c->stash->{photo_error} = _('Please upload a JPEG image only');
+ return;
+ }
+
+ # convert the photo into a blob (also resize etc)
+ my $photo_blob =
+ eval { _process_photo( $upload->fh, $args->{rotate_photo} ) };
+ if ( my $error = $@ ) {
+ my $format = _(
+"That image doesn't appear to have uploaded correctly (%s), please try again."
+ );
+ $c->stash->{photo_error} = sprintf( $format, $error );
+ return;
+ }
+
+ # we have an image we can use - save it to the cache in case there is an
+ # error
+ my $cache_dir = dir( $c->config->{UPLOAD_CACHE} );
+ $cache_dir->mkpath;
+ unless ( -d $cache_dir && -w $cache_dir ) {
+ warn "Can't find/write to photo cache directory '$cache_dir'";
+ return;
+ }
+
+ # create a random name and store the file there
+ my $fileid = int rand 1_000_000_000;
+ my $file = $cache_dir->file("$fileid.jpg");
+ $file->openw->print($photo_blob);
+
+ # stick the random number on the stash
+ $c->stash->{upload_fileid} = $fileid;
+
+ return 1;
+}
+
+=head2 process_photo_cache
+
+Look for the upload_fileid parameter and check it matches a file on disk. If it
+does return true and put fileid on stash, otherwise false.
+
+=cut
+
+sub process_photo_cache : Private {
+ my ( $self, $c ) = @_;
+
+ # get the fileid and make sure it is just a number
+ my $fileid = $c->req->param('upload_fileid') || '';
+ $fileid =~ s{\D+}{}g;
+ return unless $fileid;
+
+ my $file = file( $c->config->{UPLOAD_CACHE}, "$fileid.jpg" );
+ return unless -e $file;
+
+ $c->stash->{upload_fileid} = $fileid;
+ return 1;
+}
+
+=head2 check_for_errors
+
+Examine the user and the report for errors. If found put them on stash and
+return false.
+
+=cut
+
+sub check_for_errors : Private {
+ my ( $self, $c ) = @_;
+
+ # let the model check for errors
+ my %field_errors = (
+ %{ $c->stash->{report_user}->check_for_errors },
+ %{ $c->stash->{report}->check_for_errors },
+ );
+
+ # add the photo error if there is one.
+ if ( my $photo_error = delete $c->stash->{photo_error} ) {
+ $field_errors{photo} = $photo_error;
+ }
+
+ # all good if no errors
+ return 1 unless scalar keys %field_errors;
+
+ $c->stash->{field_errors} = \%field_errors;
+
+ return;
+}
+
+=head2 save_user_and_report
+
+Save the user and the report.
+
+Be smart about the user - only set the name and phone if user did not exist
+before or they are currently logged in. Otherwise discard any changes.
+
+=cut
+
+sub save_user_and_report : Private {
+ my ( $self, $c ) = @_;
+ my $report_user = $c->stash->{report_user};
+ my $report = $c->stash->{report};
+
+ # Save or update the user if appropriate
+ if ( !$report_user->in_storage ) {
+ $report_user->insert();
+ }
+ elsif ( $c->user && $report_user->id == $c->user->id ) {
+ $report_user->update();
+ $report->confirm;
+ }
+ else {
+
+ # user exists and we are not logged in as them. Throw away changes to
+ # the name and phone. TODO - propagate changes using tokens.
+ $report_user->discard_changes();
+ }
+
+ # add the user to the report
+ $report->user($report_user);
+
+ # If there was a photo add that too
+ if ( my $fileid = $c->stash->{upload_fileid} ) {
+ my $file = file( $c->config->{UPLOAD_CACHE}, "$fileid.jpg" );
+ my $blob = $file->slurp;
+ $file->remove;
+ $report->photo($blob);
+ }
+
+ # Set a default if possible
+ $report->category( _('Other') ) unless $report->category;
+
+ # Set unknown to DB unknown
+ $report->council( undef ) if $report->council == -1;
+
+ # save the report;
+ $report->in_storage ? $report->update : $report->insert();
+
+ # tidy up
+ if ( my $token = $c->stash->{partial_token} ) {
+ $token->delete;
+ }
+
+ return 1;
+}
+
+=head2 generate_map
+
+Add the html needed to for the map to the stash.
+
+=cut
+
+# Perhaps also create a map 'None' to use when map is skipped.
+
+sub generate_map : Private {
+ my ( $self, $c ) = @_;
+ my $latitude = $c->stash->{latitude};
+ my $longitude = $c->stash->{longitude};
+
+ ( $c->stash->{short_latitude}, $c->stash->{short_longitude} ) =
+ map { Utils::truncate_coordinate($_) }
+ ( $c->stash->{latitude}, $c->stash->{longitude} );
+
+ # Don't do anything if the user skipped the map
+ unless ( $c->req->param('skipped') ) {
+ FixMyStreet::Map::display_map(
+ $c,
+ latitude => $latitude,
+ longitude => $longitude,
+ clickable => 1,
+ pins => [ {
+ latitude => $latitude,
+ longitude => $longitude,
+ colour => 'purple',
+ } ],
+ );
+ }
+
+ return 1;
+}
+
+=head2 redirect_or_confirm_creation
+
+Now that the report has been created either redirect the user to its page if it
+has been confirmed or email them a token if it has not been.
+
+=cut
+
+sub redirect_or_confirm_creation : Private {
+ my ( $self, $c ) = @_;
+ my $report = $c->stash->{report};
+
+ # If confirmed send the user straight there.
+ if ( $report->confirmed ) {
+ # Subscribe problem reporter to email updates
+ $c->forward( 'create_reporter_alert' );
+ my $report_uri = $c->uri_for( '/report', $report->id );
+ $c->res->redirect($report_uri);
+ $c->detach;
+ }
+
+ # otherwise create a confirm token and email it to them.
+ my $token =
+ $c->model("DB::Token")
+ ->create( { scope => 'problem', data => $report->id } );
+ $c->stash->{token_url} = $c->uri_for_email( '/P', $token->token );
+ $c->send_email( 'problem-confirm.txt', {
+ to => [ [ $report->user->email, $report->name ] ],
+ } );
+
+ # tell user that they've been sent an email
+ $c->stash->{template} = 'email_sent.html';
+ $c->stash->{email_type} = 'problem';
+}
+
+sub create_reporter_alert : Private {
+ my ( $self, $c ) = @_;
+
+ my $problem = $c->stash->{report};
+ my $alert = $c->model('DB::Alert')->find_or_create( {
+ user => $problem->user,
+ alert_type => 'new_updates',
+ parameter => $problem->id,
+ cobrand => $problem->cobrand,
+ cobrand_data => $problem->cobrand_data,
+ lang => $problem->lang,
+ } )->confirm;
+}
+
+=head2 redirect_to_around
+
+Redirect the user to '/around' passing along all the relevant parameters.
+
+=cut
+
+sub redirect_to_around : Private {
+ my ( $self, $c ) = @_;
+
+ my $params = {
+ pc => ( $c->stash->{pc} || $c->req->param('pc') || '' ),
+ lat => $c->stash->{latitude},
+ lon => $c->stash->{longitude},
+ };
+
+ # delete empty values
+ for ( keys %$params ) {
+ delete $params->{$_} if !$params->{$_};
+ }
+
+ if ( my $token = $c->stash->{partial_token} ) {
+ $params->{partial} = $token->token;
+ }
+
+ my $around_uri = $c->uri_for( '/around', $params );
+
+ return $c->res->redirect($around_uri);
+}
+
+sub _process_photo {
+ my $fh = shift;
+ my $import = shift;
+
+ my $blob = join('', <$fh>);
+ close $fh;
+ my ($handle, $filename) = mySociety::TempFiles::named_tempfile('.jpeg');
+ print $handle $blob;
+ close $handle;
+
+ my $photo = Image::Magick->new;
+ my $err = $photo->Read($filename);
+ unlink $filename;
+ throw Error::Simple("read failed: $err") if "$err";
+ $err = $photo->Scale(geometry => "250x250>");
+ throw Error::Simple("resize failed: $err") if "$err";
+ my @blobs = $photo->ImageToBlob();
+ undef $photo;
+ $photo = $blobs[0];
+ return $photo unless $import; # Only check orientation for iPhone imports at present
+
+ # Now check if it needs orientating
+ ($fh, $filename) = mySociety::TempFiles::named_tempfile('.jpeg');
+ print $fh $photo;
+ close $fh;
+ my $out = `jhead -se -autorot $filename`;
+ if ($out) {
+ open(FP, $filename) or throw Error::Simple($!);
+ $photo = join('', <FP>);
+ close FP;
+ }
+ unlink $filename;
+ return $photo;
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Report/Update.pm b/perllib/FixMyStreet/App/Controller/Report/Update.pm
new file mode 100644
index 000000000..2f1d88d08
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Report/Update.pm
@@ -0,0 +1,321 @@
+package FixMyStreet::App::Controller::Report::Update;
+
+use Moose;
+use namespace::autoclean;
+BEGIN { extends 'Catalyst::Controller'; }
+
+use Path::Class;
+use Utils;
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Report::Update
+
+=head1 DESCRIPTION
+
+Creates an update to a report
+
+=cut
+
+sub report_update : Path : Args(0) {
+ my ( $self, $c ) = @_;
+
+ # if there's no id then we should just stop now
+ $c->detach( '/page_error_404_not_found', [ _('Unknown problem ID') ] )
+ unless $c->req->param('id');
+
+ $c->forward( '/report/load_problem_or_display_error', [ $c->req->param('id') ] )
+ && $c->forward('process_user')
+ && $c->forward('process_update')
+ && $c->forward('/report/new/process_photo')
+ && $c->forward('check_for_errors')
+ or $c->go( '/report/display', [ $c->req->param('id') ] );
+
+ return $c->forward('save_update')
+ && $c->forward('redirect_or_confirm_creation');
+}
+
+sub confirm : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{update}->confirm;
+ $c->stash->{update}->update;
+
+ $c->forward('update_problem');
+ $c->forward('signup_for_alerts');
+
+ return 1;
+}
+
+sub update_problem : Private {
+ my ( $self, $c ) = @_;
+
+ my $display_questionnaire = 0;
+
+ my $update = $c->stash->{update};
+ my $problem = $c->stash->{problem} || $update->problem;
+
+ if ( $update->mark_fixed ) {
+ $problem->state('fixed');
+
+ if ( $update->user->id == $problem->user->id ) {
+ $problem->send_questionnaire(0);
+
+ if ( $c->cobrand->ask_ever_reported
+ && !$problem->user->answered_ever_reported )
+ {
+ $display_questionnaire = 1;
+ }
+ }
+ }
+
+ $problem->lastupdate( \'ms_current_timestamp()' );
+ $problem->update;
+
+ $c->stash->{problem_id} = $problem->id;
+
+ if ($display_questionnaire) {
+ $c->detach('/questionnaire/creator_fixed');
+ }
+
+ return 1;
+}
+
+sub display_confirmation : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{template} = 'tokens/confirm_update.html';
+
+ return 1;
+}
+
+=head2 process_user
+
+Load user from the database or prepare a new one.
+
+=cut
+
+sub process_user : Private {
+ my ( $self, $c ) = @_;
+
+ # FIXME - If user already logged in use them regardless
+
+ # Extract all the params to a hash to make them easier to work with
+ my %params = #
+ map { $_ => scalar $c->req->param($_) } #
+ ( 'rznvy', 'name' );
+
+ # cleanup the email address
+ my $email = $params{rznvy} ? lc $params{rznvy} : '';
+ $email =~ s{\s+}{}g;
+
+ my $update_user = $c->model('DB::User')->find_or_new( { email => $email } );
+
+ # set the user's name if they don't have one
+ $update_user->name( Utils::trim_text( $params{name} ) )
+ unless $update_user->name;
+
+ $c->stash->{update_user} = $update_user;
+ $c->stash->{email} = $update_user->email;
+
+ return 1;
+}
+
+=head2 process_update
+
+Take the submitted params and create a new update item. Does not save
+anything to the database.
+
+NB: relies on their being a problem and update_user in the stash. May
+want to move adding these elsewhere
+
+=cut
+
+sub process_update : Private {
+ my ( $self, $c ) = @_;
+
+ my %params =
+ map { $_ => scalar $c->req->param($_) } ( 'update', 'name', 'fixed' );
+
+ $params{update} =
+ Utils::cleanup_text( $params{update}, { allow_multiline => 1 } );
+
+ my $name = Utils::trim_text( $params{name} );
+
+ my $anonymous = 1;
+
+ $anonymous = 0 if ( $name && $c->req->param('may_show_name') );
+
+ my $update = $c->model('DB::Comment')->new(
+ {
+ text => $params{update},
+ name => $name,
+ problem => $c->stash->{problem},
+ user => $c->stash->{update_user},
+ state => 'unconfirmed',
+ mark_fixed => $params{fixed} ? 1 : 0,
+ cobrand => $c->cobrand->moniker,
+ cobrand_data => $c->cobrand->extra_update_data,
+ lang => $c->stash->{lang_code},
+ anonymous => $anonymous,
+ }
+ );
+
+ $c->stash->{update} = $update;
+ $c->stash->{update_text} = $update->text;
+ $c->stash->{add_alert} = $c->req->param('add_alert');
+ $c->stash->{may_show_name} = ' checked' if $c->req->param('may_show_name');
+
+ return 1;
+}
+
+
+=head2 check_for_errors
+
+Examine the user and the report for errors. If found put them on stash and
+return false.
+
+=cut
+
+sub check_for_errors : Private {
+ my ( $self, $c ) = @_;
+
+ # let the model check for errors
+ my %field_errors = (
+ %{ $c->stash->{update_user}->check_for_errors },
+ %{ $c->stash->{update}->check_for_errors },
+ );
+
+ # we don't care if there are errors with this...
+ delete $field_errors{name};
+
+ # all good if no errors
+ return 1
+ unless ( scalar keys %field_errors
+ || ( $c->stash->{errors} && scalar @{ $c->stash->{errors} } )
+ || $c->stash->{photo_error} );
+
+ $c->stash->{field_errors} = \%field_errors;
+
+ $c->stash->{errors} ||= [];
+ push @{ $c->stash->{errors} },
+ _('There were problems with your update. Please see below.');
+
+ return;
+}
+
+=head2 save_update
+
+Save the update and the user as appropriate.
+
+=cut
+
+sub save_update : Private {
+ my ( $self, $c ) = @_;
+
+ my $user = $c->stash->{update_user};
+ my $update = $c->stash->{update};
+
+ if ( !$user->in_storage ) {
+ $user->insert;
+ }
+ elsif ( $c->user && $c->user->id == $user->id ) {
+ $user->update;
+ $update->confirm;
+ }
+
+ # If there was a photo add that too
+ if ( my $fileid = $c->stash->{upload_fileid} ) {
+ my $file = file( $c->config->{UPLOAD_CACHE}, "$fileid.jpg" );
+ my $blob = $file->slurp;
+ $file->remove;
+ $update->photo($blob);
+ }
+
+ if ( $update->in_storage ) {
+ $update->update;
+ }
+ else {
+ $update->insert;
+ }
+
+ return 1;
+}
+
+=head2 redirect_or_confirm_creation
+
+Now that the update has been created either redirect the user to problem page if it
+has been confirmed or email them a token if it has not been.
+
+=cut
+
+sub redirect_or_confirm_creation : Private {
+ my ( $self, $c ) = @_;
+ my $update = $c->stash->{update};
+
+ # If confirmed send the user straight there.
+ if ( $update->confirmed ) {
+ $c->forward( 'signup_for_alerts' );
+ $c->forward( 'update_problem' );
+ my $report_uri = $c->uri_for( '/report', $update->problem_id );
+ $c->res->redirect($report_uri);
+ $c->detach;
+ }
+
+ # otherwise create a confirm token and email it to them.
+ my $token = $c->model("DB::Token")->create(
+ {
+ scope => 'comment',
+ data => {
+ id => $update->id,
+ add_alert => ( $c->req->param('add_alert') ? 1 : 0 ),
+ }
+ }
+ );
+ $c->stash->{token_url} = $c->uri_for_email( '/C', $token->token );
+ $c->send_email( 'update-confirm.txt', {
+ to => $update->name
+ ? [ [ $update->user->email, $update->name ] ]
+ : $update->user->email,
+ } );
+
+ # tell user that they've been sent an email
+ $c->stash->{template} = 'email_sent.html';
+ $c->stash->{email_type} = 'update';
+
+ return 1;
+}
+
+=head2 signup_for_alerts
+
+If the user has selected to be signed up for alerts then create a
+new_updates alert.
+
+NB: this does not check if they are a registered user so that should
+happen before calling this.
+
+=cut
+
+sub signup_for_alerts : Private {
+ my ( $self, $c ) = @_;
+
+ if ( $c->stash->{add_alert} ) {
+ my $update = $c->stash->{update};
+ my $alert = $c->model('DB::Alert')->find_or_create(
+ user => $update->user,
+ alert_type => 'new_updates',
+ parameter => $update->problem_id,
+ cobrand => $update->cobrand,
+ cobrand_data => $update->cobrand_data,
+ lang => $update->lang,
+ );
+
+ $alert->confirm();
+ }
+
+ return 1;
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm
new file mode 100644
index 000000000..f2bb13ae4
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Reports.pm
@@ -0,0 +1,359 @@
+package FixMyStreet::App::Controller::Reports;
+use Moose;
+use namespace::autoclean;
+
+use POSIX qw(strcoll);
+use mySociety::MaPit;
+use mySociety::VotingArea;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Reports - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+=head2 index
+
+Show the summary page of all reports.
+
+=cut
+
+sub index : Path : Args(0) {
+ my ( $self, $c ) = @_;
+
+ $c->response->header('Cache-Control' => 'max-age=3600');
+
+ # Fetch all areas of the types we're interested in
+ my @area_types = $c->cobrand->area_types;
+ my $areas_info = mySociety::MaPit::call('areas', \@area_types,
+ min_generation => $c->cobrand->area_min_generation
+ );
+
+ # For each area, add its link and perhaps alter its name if we need to for
+ # places with the same name.
+ foreach (values %$areas_info) {
+ $_->{url} = $c->uri_for( '/reports/' . $c->cobrand->short_name( $_, $areas_info ) );
+ if ($_->{parent_area} && $_->{url} =~ /,|%2C/) {
+ $_->{name} .= ', ' . $areas_info->{$_->{parent_area}}{name};
+ }
+ }
+
+ $c->stash->{areas_info} = $areas_info;
+ my @keys = sort { strcoll($areas_info->{$a}{name}, $areas_info->{$b}{name}) } keys %$areas_info;
+ $c->stash->{areas_info_sorted} = [ map { $areas_info->{$_} } @keys ];
+
+ $c->forward( 'load_problems' );
+ $c->forward( 'group_problems' );
+}
+
+=head2 index
+
+Show the summary page for a particular council.
+
+=cut
+
+sub council : Path : Args(1) {
+ my ( $self, $c, $council ) = @_;
+ $c->detach( 'ward', [ $council ] );
+}
+
+=head2 index
+
+Show the summary page for a particular ward.
+
+=cut
+
+sub ward : Path : Args(2) {
+ my ( $self, $c, $council, $ward ) = @_;
+
+ $c->forward( 'council_check', [ $council ] );
+ $c->forward( 'ward_check', [ $ward ] )
+ if $ward;
+ $c->forward( 'load_parent' );
+ $c->forward( 'load_problems' );
+ $c->forward( 'group_problems' );
+ $c->forward( 'sort_problems' );
+
+ $c->stash->{rss_url} = '/rss/reports/'
+ . $c->cobrand->short_name( $c->stash->{council}, $c->stash->{areas_info} );
+ $c->stash->{rss_url} .= '/' . $c->cobrand->short_name( $c->stash->{ward} )
+ if $c->stash->{ward};
+}
+
+sub rss_council : Regex('^rss/(reports|area)$') : Args(1) {
+ my ( $self, $c, $council ) = @_;
+ $c->detach( 'rss_ward', [ $council ] );
+}
+
+sub rss_ward : Regex('^rss/(reports|area)$') : Args(2) {
+ my ( $self, $c, $council, $ward ) = @_;
+
+ my ( $rss ) = $c->req->captures->[0];
+
+ $c->stash->{rss} = 1;
+
+ $c->forward( 'council_check', [ $council ] );
+ $c->forward( 'ward_check', [ $ward ] ) if $ward;
+
+ if ($rss eq 'area' && $c->stash->{council}{type} ne 'DIS' && $c->stash->{council}{type} ne 'CTY') {
+ # Two possibilites are the same for one-tier councils, so redirect one to the other
+ $c->detach( 'redirect_area' );
+ }
+
+ my $url = $c->cobrand->short_name( $c->stash->{council} );
+ $url .= '/' . $c->cobrand->short_name( $c->stash->{ward} ) if $c->stash->{ward};
+ $c->stash->{qs} = "/$url";
+
+ my @params;
+ push @params, $c->stash->{council}->{id} if $rss eq 'reports';
+ push @params, $c->stash->{ward}
+ ? $c->stash->{ward}->{id}
+ : $c->stash->{council}->{id};
+ $c->stash->{db_params} = [ @params ];
+
+ if ( $rss eq 'area' && $c->stash->{ward} ) {
+ # All problems within a particular ward
+ $c->stash->{type} = 'area_problems';
+ $c->stash->{title_params} = { NAME => $c->stash->{ward}{name} };
+ $c->stash->{db_params} = [ $c->stash->{ward}->{id} ];
+ } elsif ( $rss eq 'area' ) {
+ # Problems within a particular council
+ $c->stash->{type} = 'area_problems';
+ $c->stash->{title_params} = { NAME => $c->stash->{council}{name} };
+ $c->stash->{db_params} = [ $c->stash->{council}->{id} ];
+ } elsif ($c->stash->{ward}) {
+ # Problems sent to a council, restricted to a ward
+ $c->stash->{type} = 'ward_problems';
+ $c->stash->{title_params} = { COUNCIL => $c->stash->{council}{name}, WARD => $c->stash->{ward}{name} };
+ $c->stash->{db_params} = [ $c->stash->{council}->{id}, $c->stash->{ward}->{id} ];
+ } else {
+ # Problems sent to a council
+ $c->stash->{type} = 'council_problems';
+ $c->stash->{title_params} = { COUNCIL => $c->stash->{council}{name} };
+ $c->stash->{db_params} = [ $c->stash->{council}->{id}, $c->stash->{council}->{id} ];
+ }
+
+ # Send on to the RSS generation
+ $c->forward( '/rss/output' );
+}
+
+=head2 council_check
+
+This action checks the council name (or code) given in a URI exists, is valid
+and so on. If it is, it stores the Area in the stash, otherwise it redirects
+to the all reports page.
+
+=cut
+
+sub council_check : Private {
+ my ( $self, $c, $q_council ) = @_;
+
+ $q_council =~ s/\+/ /g;
+
+ # Manual misspelling redirect
+ if ($q_council =~ /^rhondda cynon taff$/i) {
+ my $url = $c->uri_for( '/reports/rhondda+cynon+taf' );
+ $c->res->redirect( $url );
+ $c->detach();
+ }
+
+ # Check cobrand specific incantations - e.g. ONS codes for UK,
+ # Oslo/ kommunes sharing a name in Norway
+ return if $c->cobrand->reports_council_check( $c, $q_council );
+
+ # If we're passed an ID number (don't think this is used anywhere, it
+ # certainly shouldn't be), just look that up on MaPit and redirect
+ if ($q_council =~ /^\d+$/) {
+ my $council = mySociety::MaPit::call('area', $q_council);
+ $c->detach( 'redirect_index') if $council->{error};
+ $c->stash->{council} = $council;
+ $c->detach( 'redirect_area' );
+ }
+
+ # We must now have a string to check
+ my @area_types = $c->cobrand->area_types;
+ my $areas = mySociety::MaPit::call( 'areas', $q_council,
+ type => \@area_types,
+ min_generation => $c->cobrand->area_min_generation
+ );
+ if (keys %$areas == 1) {
+ ($c->stash->{council}) = values %$areas;
+ return;
+ } else {
+ foreach (keys %$areas) {
+ if ($areas->{$_}->{name} eq $q_council || $areas->{$_}->{name} =~ /^\Q$q_council\E (Borough|City|District|County) Council$/) {
+ $c->stash->{council} = $areas->{$_};
+ return;
+ }
+ }
+ }
+
+ # No result, bad council name.
+ $c->detach( 'redirect_index' );
+}
+
+=head2 ward_check
+
+This action checks the ward name from a URI exists and is part of the right
+parent, already found with council_check. It either stores the ward Area if
+okay, or redirects to the council page if bad.
+This is currently only used in the UK, hence the use of mySociety::VotingArea.
+
+=cut
+
+sub ward_check : Private {
+ my ( $self, $c, $ward ) = @_;
+
+ $ward =~ s/\+/ /g;
+ my $council = $c->stash->{council};
+
+ my $qw = mySociety::MaPit::call('areas', $ward,
+ type => $mySociety::VotingArea::council_child_types,
+ min_generation => $c->cobrand->area_min_generation
+ );
+ foreach my $id (sort keys %$qw) {
+ if ($qw->{$id}->{parent_area} == $council->{id}) {
+ $c->stash->{ward} = $qw->{$id};
+ return;
+ }
+ }
+ # Given a false ward name
+ $c->detach( 'redirect_area' );
+}
+
+sub load_parent : Private {
+ my ( $self, $c ) = @_;
+
+ my $council = $c->stash->{council};
+ my $areas_info;
+ if ($council->{parent_area}) {
+ $c->stash->{areas_info} = mySociety::MaPit::call('areas', [ $council->{id}, $council->{parent_area} ])
+ } else {
+ $c->stash->{areas_info} = { $council->{id} => $council };
+ }
+}
+
+sub load_problems : Private {
+ my ( $self, $c ) = @_;
+
+ my $where = {
+ state => [ 'confirmed', 'fixed' ]
+ };
+ if ($c->stash->{ward}) {
+ $where->{areas} = { 'like', '%' . $c->stash->{ward}->{id} . '%' }; # FIXME Check this is secure
+ } elsif ($c->stash->{council}) {
+ $where->{areas} = { 'like', '%' . $c->stash->{council}->{id} . '%' };
+ }
+ my $problems = $c->cobrand->problems->search(
+ $where,
+ {
+ columns => [
+ 'id', 'title', 'detail', 'council', 'state', 'areas',
+ { duration => { extract => "epoch from current_timestamp-lastupdate" } },
+ { age => { extract => "epoch from current_timestamp-confirmed" } },
+ ],
+ order_by => { -desc => 'id' },
+ }
+ );
+ $c->stash->{problems} = [ $problems->all ];
+
+ return 1;
+}
+
+sub group_problems : Private {
+ my ( $self, $c ) = @_;
+
+ my ( %fixed, %open );
+ my $re_councils = join('|', keys %{$c->stash->{areas_info}});
+ foreach my $row (@{$c->stash->{problems}}) {
+ if (!$row->council) {
+ # Problem was not sent to any council, add to possible councils
+ my $areas = $row->areas;
+ while ($areas =~ /,($re_councils)(?=,)/g) {
+ add_row($row, $1, \%fixed, \%open);
+ }
+ } else {
+ # Add to councils it was sent to
+ foreach ($row->councils) {
+ next if $c->stash->{council} && $_ != $c->stash->{council}->{id};
+ add_row($row, $_, \%fixed, \%open);
+ }
+ }
+ }
+
+ $c->stash->{fixed} = \%fixed;
+ $c->stash->{open} = \%open;
+
+ return 1;
+}
+
+sub sort_problems : Private {
+ my ( $self, $c ) = @_;
+
+ my $id = $c->stash->{council}->{id};
+ my $fixed = $c->stash->{fixed};
+ my $open = $c->stash->{open};
+
+ foreach (qw/new old/) {
+ $c->stash->{fixed}{$id}{$_} = [ sort { $a->get_column('duration') <=> $b->get_column('duration') } @{$fixed->{$id}{$_}} ]
+ if $fixed->{$id}{$_};
+ }
+ foreach (qw/new older unknown/) {
+ $c->stash->{open}{$id}{$_} = [ sort { $a->get_column('age') <=> $b->get_column('age') } @{$open->{$id}{$_}} ]
+ if $open->{$id}{$_};
+ }
+}
+
+sub redirect_index : Private {
+ my ( $self, $c ) = @_;
+ my $url = '/reports';
+ $c->res->redirect( $c->uri_for($url) );
+}
+
+sub redirect_area : Private {
+ my ( $self, $c ) = @_;
+ my $url = '';
+ $url .= "/rss" if $c->stash->{rss};
+ $url .= '/reports';
+ $url .= '/' . $c->cobrand->short_name( $c->stash->{council} );
+ $url .= '/' . $c->cobrand->short_name( $c->stash->{ward} )
+ if $c->stash->{ward};
+ $c->res->redirect( $c->uri_for($url) );
+}
+
+sub add_row {
+ my ($row, $council, $fixed, $open) = @_;
+ my $fourweeks = 4*7*24*60*60;
+ my $duration = ($row->get_column('duration') > 2 * $fourweeks) ? 'old' : 'new';
+ my $type = ($row->get_column('duration') > 2 * $fourweeks)
+ ? 'unknown'
+ : ($row->get_column('age') > $fourweeks ? 'older' : 'new');
+ # Fixed problems are either old or new
+ push @{$fixed->{$council}{$duration}}, $row if $row->state eq 'fixed';
+ # Open problems are either unknown, older, or new
+ push @{$open->{$council}{$type}}, $row if $row->state eq 'confirmed';
+}
+
+=head1 AUTHOR
+
+Matthew Somerville
+
+=head1 LICENSE
+
+Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved.
+Licensed under the Affero GPL.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;
+
diff --git a/perllib/FixMyStreet/App/Controller/Root.pm b/perllib/FixMyStreet/App/Controller/Root.pm
new file mode 100644
index 000000000..7e2acc03c
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Root.pm
@@ -0,0 +1,108 @@
+package FixMyStreet::App::Controller::Root;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller' }
+
+__PACKAGE__->config( namespace => '' );
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Root - Root Controller for FixMyStreet::App
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 METHODS
+
+=head2 auto
+
+Set up general things for this instance
+
+=cut
+
+sub auto : Private {
+ my ( $self, $c ) = @_;
+
+ # decide which cobrand this request should use
+ $c->setup_request();
+
+ return 1;
+}
+
+=head2 index
+
+Home page.
+
+If request includes certain parameters redirect to '/around' - this is to
+preserve old behaviour.
+
+=cut
+
+sub index : Path : Args(0) {
+ my ( $self, $c ) = @_;
+
+ my @old_param_keys = ( 'pc', 'x', 'y', 'e', 'n', 'lat', 'lon' );
+ my %old_params = ();
+
+ foreach my $key (@old_param_keys) {
+ my $val = $c->req->param($key);
+ next unless $val;
+ $old_params{$key} = $val;
+ }
+
+ if ( scalar keys %old_params ) {
+ my $around_uri = $c->uri_for( '/around', \%old_params );
+ $c->res->redirect($around_uri);
+ return;
+ }
+
+}
+
+=head2 default
+
+Forward to the standard 404 error page
+
+=cut
+
+sub default : Path {
+ my ( $self, $c ) = @_;
+ $c->detach('/page_error_404_not_found');
+}
+
+=head2 page_error_404_not_found, page_error_410_gone
+
+ $c->detach( '/page_error_404_not_found', [$error_msg] );
+ $c->detach( '/page_error_410_gone', [$error_msg] );
+
+Display a 404 (not found) or 410 (gone) page. Pass in an optional error message in an arrayref.
+
+=cut
+
+sub page_error_404_not_found : Private {
+ my ( $self, $c, $error_msg ) = @_;
+ $c->stash->{template} = 'errors/page_error_404_not_found.html';
+ $c->stash->{error_msg} = $error_msg;
+ $c->response->status(404);
+}
+
+sub page_error_410_gone : Private {
+ my ( $self, $c, $error_msg ) = @_;
+ $c->stash->{template} = 'errors/page_error_410_gone.html';
+ $c->stash->{error_msg} = $error_msg;
+ $c->response->status(410);
+}
+
+=head2 end
+
+Attempt to render a view, if needed.
+
+=cut
+
+sub end : ActionClass('RenderView') {
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Rss.pm b/perllib/FixMyStreet/App/Controller/Rss.pm
new file mode 100755
index 000000000..0edded1e3
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Rss.pm
@@ -0,0 +1,340 @@
+package FixMyStreet::App::Controller::Rss;
+
+use Moose;
+use namespace::autoclean;
+use POSIX qw(strftime);
+use URI::Escape;
+use XML::RSS;
+
+use mySociety::Gaze;
+use mySociety::Locale;
+use mySociety::MaPit;
+use mySociety::Sundries qw(ordinal);
+use mySociety::Web qw(ent);
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Rss - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+sub updates : LocalRegex('^(\d+)$') {
+ my ( $self, $c ) = @_;
+
+ my $id = $c->req->captures->[0];
+ $c->forward( '/report/load_problem_or_display_error', [ $id ] );
+
+ $c->stash->{type} = 'new_updates';
+ $c->stash->{qs} = 'report/' . $id;
+ $c->stash->{db_params} = [ $id ];
+ $c->forward('output');
+}
+
+sub new_problems : Path('problems') : Args(0) {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{type} = 'new_problems';
+ $c->forward('output');
+}
+
+# FIXME I don't think this is used - check
+#sub reports_to_council : Private {
+# my ( $self, $c ) = @_;
+#
+# my $id = $c->stash->{id};
+# $c->stash->{type} = 'council_problems';
+# $c->stash->{qs} = '/' . $id;
+# $c->stash->{db_params} = [ $id ];
+# $c->forward('output');
+#}
+
+sub reports_in_area : LocalRegex('^area/(\d+)$') {
+ my ( $self, $c ) = @_;
+
+ my $id = $c->req->captures->[0];
+ my $area = mySociety::MaPit::call('area', $id);
+ $c->stash->{type} = 'area_problems';
+ $c->stash->{qs} = '/' . $id;
+ $c->stash->{db_params} = [ $id ];
+ $c->stash->{title_params} = { NAME => $area->{name} };
+ $c->forward('output');
+}
+
+sub all_problems : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{type} = 'all_problems';
+ $c->forward('output');
+}
+
+sub local_problems_pc : Path('pc') : Args(1) {
+ my ( $self, $c, $query ) = @_;
+ $c->forward( 'local_problems_pc_distance', [ $query ] );
+}
+
+sub local_problems_pc_distance : Path('pc') : Args(2) {
+ my ( $self, $c, $query, $d ) = @_;
+
+ $c->forward( 'get_query_parameters', [ $d ] );
+ unless ( $c->forward( '/location/determine_location_from_pc', [ $query ] ) ) {
+ $c->res->redirect( '/alert' );
+ $c->detach();
+ }
+
+ my $pretty_query = $query;
+ $pretty_query = mySociety::PostcodeUtil::canonicalise_postcode($query)
+ if mySociety::PostcodeUtil::is_valid_postcode($query);
+
+ my $pretty_query_escaped = URI::Escape::uri_escape_utf8($pretty_query);
+ $pretty_query_escaped =~ s/%20/+/g;
+
+ $c->stash->{qs} = "?pc=$pretty_query_escaped";
+ $c->stash->{title_params} = { POSTCODE => $pretty_query };
+ $c->stash->{type} = 'postcode_local_problems';
+
+ $c->forward( 'local_problems_ll',
+ [ $c->stash->{latitude}, $c->stash->{longitude} ]
+ );
+
+}
+
+sub local_problems : LocalRegex('^(n|l)/([\d.-]+)[,/]([\d.-]+)(?:/(\d+))?$') {
+ my ( $self, $c ) = @_;
+
+ my ( $type, $a, $b, $d) = @{ $c->req->captures };
+ $c->forward( 'get_query_parameters', [ $d ] );
+
+ $c->detach( 'redirect_lat_lon', [ $a, $b ] )
+ if $type eq 'n';
+
+ $c->stash->{qs} = "?lat=$a;lon=$b";
+ $c->stash->{type} = 'local_problems';
+
+ $c->forward( 'local_problems_ll', [ $a, $b ] );
+}
+
+sub local_problems_ll : Private {
+ my ( $self, $c, $lat, $lon ) = @_;
+
+ # truncate the lat,lon for nicer urls
+ ( $lat, $lon ) = map { Utils::truncate_coordinate($_) } ( $lat, $lon );
+
+ my $d = $c->stash->{distance};
+ if ( $d ) {
+ $c->stash->{qs} .= ";d=$d";
+ $d = 100 if $d > 100;
+ } else {
+ $d = mySociety::Gaze::get_radius_containing_population( $lat, $lon, 200000 );
+ $d = int( $d * 10 + 0.5 ) / 10;
+ mySociety::Locale::in_gb_locale {
+ $d = sprintf("%f", $d);
+ }
+ }
+
+ $c->stash->{db_params} = [ $lat, $lon, $d ];
+
+ if ($c->stash->{state} ne 'all') {
+ $c->stash->{type} .= '_state';
+ push @{ $c->stash->{db_params} }, $c->stash->{state};
+ }
+
+ $c->forward('output');
+}
+
+sub output : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{alert_type} = $c->model('DB::AlertType')->find( { ref => $c->stash->{type} } );
+ $c->detach( '/page_error_404_not_found', [ _('Unknown alert type') ] )
+ unless $c->stash->{alert_type};
+
+ $c->forward( 'query_main' );
+
+ # Do our own encoding
+ $c->stash->{rss} = new XML::RSS(
+ version => '2.0',
+ encoding => 'UTF-8',
+ stylesheet => $c->cobrand->feed_xsl,
+ encode_output => undef
+ );
+ $c->stash->{rss}->add_module(
+ prefix => 'georss',
+ uri => 'http://www.georss.org/georss'
+ );
+
+ while (my $row = $c->stash->{query_main}->fetchrow_hashref) {
+ $c->forward( 'add_row', [ $row ] );
+ }
+
+ $c->forward( 'add_parameters' );
+
+ my $out = $c->stash->{rss}->as_string;
+ my $uri = $c->uri_for( '/' . $c->req->path );
+ $out =~ s{<link>(.*?)</link>}{"<link>" . $c->uri_for( $1 ) . "</link><uri>$uri</uri>"}e;
+
+ $c->response->header('Content-Type' => 'application/xml; charset=utf-8');
+ $c->response->body( $out );
+}
+
+sub query_main : Private {
+ my ( $self, $c ) = @_;
+ my $alert_type = $c->stash->{alert_type};
+
+ my ( $site_restriction, $site_id ) = $c->cobrand->site_restriction( $c->cobrand->extra_data );
+ # Only apply a site restriction if the alert uses the problem table
+ $site_restriction = '' unless $alert_type->item_table eq 'problem';
+
+ # FIXME Do this in a nicer way at some point in the future...
+ my $query = 'select * from ' . $alert_type->item_table . ' where '
+ . ($alert_type->head_table ? $alert_type->head_table . '_id=? and ' : '')
+ . $alert_type->item_where . $site_restriction . ' order by '
+ . $alert_type->item_order;
+ my $rss_limit = mySociety::Config::get('RSS_LIMIT');
+ $query .= " limit $rss_limit" unless $c->stash->{type} =~ /^all/;
+
+ my $q = $c->model('DB::Alert')->result_source->storage->dbh->prepare($query);
+
+ $c->stash->{db_params} ||= [];
+ if ($query =~ /\?/) {
+ $c->detach( '/page_error_404_not_found', [ 'Missing parameter' ] )
+ unless @{ $c->stash->{db_params} };
+ $q->execute( @{ $c->stash->{db_params} } );
+ } else {
+ $q->execute();
+ }
+ $c->stash->{query_main} = $q;
+}
+
+sub add_row : Private {
+ my ( $self, $c, $row ) = @_;
+ my $alert_type = $c->stash->{alert_type};
+
+ $row->{name} ||= 'anonymous';
+
+ my $pubDate;
+ if ($row->{confirmed}) {
+ $row->{confirmed} =~ /^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)/;
+ $pubDate = mySociety::Locale::in_gb_locale {
+ strftime("%a, %d %b %Y %H:%M:%S %z", $6, $5, $4, $3, $2-1, $1-1900, -1, -1, 0)
+ };
+ $row->{confirmed} = strftime("%e %B", $6, $5, $4, $3, $2-1, $1-1900, -1, -1, 0);
+ $row->{confirmed} =~ s/^\s+//;
+ $row->{confirmed} =~ s/^(\d+)/ordinal($1)/e if $c->stash->{lang_code} eq 'en-gb';
+ }
+
+ (my $title = _($alert_type->item_title)) =~ s/{{(.*?)}}/$row->{$1}/g;
+ (my $link = $alert_type->item_link) =~ s/{{(.*?)}}/$row->{$1}/g;
+ (my $desc = _($alert_type->item_description)) =~ s/{{(.*?)}}/$row->{$1}/g;
+ my $url = $c->uri_for( $link );
+ my %item = (
+ title => ent($title),
+ link => $url,
+ guid => $url,
+ description => ent(ent($desc)) # Yes, double-encoded, really.
+ );
+ $item{pubDate} = $pubDate if $pubDate;
+ $item{category} = $row->{category} if $row->{category};
+
+ if ($c->cobrand->allow_photo_display && $row->{photo}) {
+ my $key = $alert_type->item_table eq 'comment' ? 'c' : 'id';
+ $item{description} .= ent("\n<br><img src=\"". $c->cobrand->base_url . "/photo?$key=$row->{id}\">");
+ }
+ my $recipient_name = $c->cobrand->contact_name;
+ $item{description} .= ent("\n<br><a href='$url'>" .
+ sprintf(_("Report on %s"), $recipient_name) . "</a>");
+
+ if ($row->{latitude} || $row->{longitude}) {
+ $item{georss} = { point => "$row->{latitude} $row->{longitude}" };
+ }
+
+ $c->stash->{rss}->add_item( %item );
+}
+
+sub add_parameters : Private {
+ my ( $self, $c ) = @_;
+ my $alert_type = $c->stash->{alert_type};
+
+ my $row = {};
+ if ($alert_type->head_sql_query) {
+ my $q = $c->model('DB::Alert')->result_source->storage->dbh->prepare(
+ $alert_type->head_sql_query
+ );
+ if ($alert_type->head_sql_query =~ /\?/) {
+ $q->execute(@{ $c->stash->{db_params} });
+ } else {
+ $q->execute();
+ }
+ $row = $q->fetchrow_hashref;
+ }
+ foreach ( keys %{ $c->stash->{title_params} } ) {
+ $row->{$_} = $c->stash->{title_params}->{$_};
+ }
+
+ (my $title = _($alert_type->head_title)) =~ s/{{(.*?)}}/$row->{$1}/g;
+ (my $link = $alert_type->head_link) =~ s/{{(.*?)}}/$row->{$1}/g;
+ (my $desc = _($alert_type->head_description)) =~ s/{{(.*?)}}/$row->{$1}/g;
+
+ $c->stash->{rss}->channel(
+ title => ent($title),
+ link => $link . ($c->stash->{qs} || ''),
+ description => ent($desc),
+ language => 'en-gb',
+ );
+}
+
+sub local_problems_legacy : LocalRegex('^(\d+)[,/](\d+)(?:/(\d+))?$') {
+ my ( $self, $c ) = @_;
+ my ($x, $y, $d) = @{ $c->req->captures };
+ $c->forward( 'get_query_parameters', [ $d ] );
+
+ # 5000/31 as initial scale factor for these RSS feeds, now variable so redirect.
+ my $e = int( ($x * 5000/31) + 0.5 );
+ my $n = int( ($y * 5000/31) + 0.5 );
+ $c->detach( 'redirect_lat_lon', [ $e, $n ] );
+}
+
+sub get_query_parameters : Private {
+ my ( $self, $c, $d ) = @_;
+
+ $d = '' unless $d =~ /^\d+$/;
+ $c->stash->{distance} = $d;
+
+ my $state = $c->req->param('state') || 'all';
+ $state = 'all' unless $state =~ /^(all|open|fixed)$/;
+ $c->stash->{state_qs} = "?state=$state" unless $state eq 'all';
+
+ $state = 'confirmed' if $state eq 'open';
+ $c->stash->{state} = $state;
+}
+
+sub redirect_lat_lon : Private {
+ my ( $self, $c, $e, $n ) = @_;
+ my ($lat, $lon) = Utils::convert_en_to_latlon_truncated($e, $n);
+
+ my $d_str = '';
+ $d_str = '/' . $c->stash->{distance} if $c->stash->{distance};
+ $c->res->redirect( "/rss/l/$lat,$lon" . $d_str . $c->stash->{state_qs} );
+}
+
+=head1 AUTHOR
+
+Matthew Somerville
+
+=head1 LICENSE
+
+Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved.
+Licensed under the Affero GPL.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Static.pm b/perllib/FixMyStreet/App/Controller/Static.pm
new file mode 100755
index 000000000..2e6bda28c
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Static.pm
@@ -0,0 +1,56 @@
+package FixMyStreet::App::Controller::Static;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Static - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Static pages Catalyst Controller. FAQ does some smarts to choose the correct
+template depending on language, will need extending at some point.
+
+=head1 METHODS
+
+=cut
+
+sub about : Global : Args(0) {
+ my ( $self, $c ) = @_;
+ # don't need to do anything here - should just pass through.
+}
+
+sub faq : Global : Args(0) {
+ my ( $self, $c ) = @_;
+
+ # There should be a faq template for each language in a cobrand or default.
+ # This is because putting the FAQ translations into the PO files is
+ # overkill.
+
+ # We rely on the list of languages for the site being restricted so that there
+ # will be a faq template for that language/cobrand combo.
+
+ my $lang_code = $c->stash->{lang_code};
+ my $template = "faq/faq-$lang_code.html";
+ $c->stash->{template} = $template;
+}
+
+sub fun : Global : Args(0) {
+ my ( $self, $c ) = @_;
+ # don't need to do anything here - should just pass through.
+}
+
+sub posters : Global : Args(0) {
+ my ( $self, $c ) = @_;
+}
+
+sub iphone : Global : Args(0) {
+ my ( $self, $c ) = @_;
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
+
diff --git a/perllib/FixMyStreet/App/Controller/Tilma.pm b/perllib/FixMyStreet/App/Controller/Tilma.pm
new file mode 100644
index 000000000..1be481949
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Tilma.pm
@@ -0,0 +1,46 @@
+package FixMyStreet::App::Controller::Tilma;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+use LWP::UserAgent;
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Tilma - Tilma proxy
+
+=head1 DESCRIPTION
+
+A tilma proxy - only intended to be used during dev. In production the webserver should do this proxying.
+
+=head1 METHODS
+
+=head2 default
+
+Proxy everything through to the tilma servers.
+
+=cut
+
+sub default : Path {
+ my ( $self, $c ) = @_;
+
+ my $path = $c->req->uri->path_query;
+ $path =~ s{/tilma/}{};
+
+ my $tilma_uri = URI->new("http://tilma.mysociety.org/$path");
+
+ my $tilma_res = LWP::UserAgent->new->get($tilma_uri);
+
+ if ( $tilma_res->is_success ) {
+ $c->res->content_type( $tilma_res->content_type );
+ $c->res->body( $tilma_res->content );
+ }
+ else {
+ die sprintf "Error getting %s: %s", $tilma_uri, $tilma_res->message;
+ }
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Tokens.pm b/perllib/FixMyStreet/App/Controller/Tokens.pm
new file mode 100644
index 000000000..c75fcc9ee
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Tokens.pm
@@ -0,0 +1,218 @@
+package FixMyStreet::App::Controller::Tokens;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Tokens - Handle auth tokens
+
+=head1 DESCRIPTION
+
+Act on the various tokens that can be submitted.
+
+=head1 METHODS
+
+=cut
+
+=head2 confirm_problem
+
+ /P/([0-9A-Za-z]{16,18}).*$
+
+Confirm a problem - url appears in emails sent to users after they create the
+problem but are not logged in.
+
+=cut
+
+sub confirm_problem : Path('/P') {
+ my ( $self, $c, $token_code ) = @_;
+
+ my $auth_token =
+ $c->forward( 'load_auth_token', [ $token_code, 'problem' ] );
+
+ # Load the problem
+ my $problem_id = $auth_token->data;
+ my $problem = $c->cobrand->problems->find( { id => $problem_id } )
+ || $c->detach('token_error');
+ $c->stash->{problem} = $problem;
+
+ # check that this email or domain are not the cause of abuse. If so hide it.
+ if ( $problem->is_from_abuser ) {
+ $problem->update(
+ { state => 'hidden', lastupdate => \'ms_current_timestamp()' } );
+ $c->stash->{template} = 'tokens/abuse.html';
+ return;
+ }
+
+ # We have a problem - confirm it if needed!
+ $problem->update(
+ {
+ state => 'confirmed',
+ confirmed => \'ms_current_timestamp()',
+ lastupdate => \'ms_current_timestamp()',
+ }
+ ) if $problem->state eq 'unconfirmed';
+
+ # Subscribe problem reporter to email updates
+ $c->stash->{report} = $c->stash->{problem};
+ $c->forward( '/report/new/create_reporter_alert' );
+
+ # log the problem creation user in to the site
+ $c->authenticate( { email => $problem->user->email }, 'no_password' );
+ $c->set_session_cookie_expire(0);
+
+ return 1;
+}
+
+=head2 redirect_to_partial_problem
+
+ /P/...
+
+Redirect user to continue filling in a partial problem. The request is sent to
+'/report/new' which might redirect again to '/around' if there is no location
+found.
+
+=cut
+
+sub redirect_to_partial_problem : Path('/L') {
+ my ( $self, $c, $token_code ) = @_;
+
+ my $url = $c->uri_for( "/report/new", { partial => $token_code } );
+ return $c->res->redirect($url);
+}
+
+=head2 confirm_alert
+
+ /A/([0-9A-Za-z]{16,18}).*$
+
+Confirm an alert - url appears in emails sent to users after they create the
+alert but are not logged in.
+
+=cut
+
+sub confirm_alert : Path('/A') {
+ my ( $self, $c, $token_code ) = @_;
+
+ my $auth_token = $c->forward( 'load_auth_token', [ $token_code, 'alert' ] );
+
+ # Load the problem
+ my $alert_id = $auth_token->data->{id};
+ $c->stash->{confirm_type} = $auth_token->data->{type};
+ my $alert = $c->model('DB::Alert')->find( { id => $alert_id } )
+ || $c->detach('token_error');
+ $c->stash->{alert} = $alert;
+
+ # check that this email or domain are not the cause of abuse. If so hide it.
+ if ( $alert->is_from_abuser ) {
+ $c->stash->{template} = 'tokens/abuse.html';
+ return;
+ }
+
+ $c->authenticate( { email => $alert->user->email }, 'no_password' );
+ $c->set_session_cookie_expire(0);
+
+ $c->forward('/alert/confirm');
+
+ return 1;
+}
+
+=head2 confirm_update
+
+ /C/([0-9A-Za-z]{16,18}).*$
+
+Confirm an update - url appears in emails sent to users after they create the
+update but are not logged in.
+
+=cut
+
+sub confirm_update : Path('/C') {
+ my ( $self, $c, $token_code ) = @_;
+
+ my $auth_token =
+ $c->forward( 'load_auth_token', [ $token_code, 'comment' ] );
+
+ # Load the problem
+ my $comment_id = $auth_token->data->{id};
+ $c->stash->{add_alert} = $auth_token->data->{add_alert};
+
+ my $comment = $c->model('DB::Comment')->find( { id => $comment_id } )
+ || $c->detach('token_error');
+ $c->stash->{update} = $comment;
+
+ # check that this email or domain are not the cause of abuse. If so hide it.
+ if ( $comment->is_from_abuser ) {
+ $c->stash->{template} = 'tokens/abuse.html';
+ return;
+ }
+
+ $c->authenticate( { email => $comment->user->email }, 'no_password' );
+ $c->set_session_cookie_expire(0);
+
+ $c->forward('/report/update/confirm');
+
+ return 1;
+}
+
+sub load_questionnaire_id : Private {
+ my ( $self, $c, $token_code ) = @_;
+
+ # Set up error handling
+ $c->stash->{error_template} = 'questionnaire/error.html';
+ $c->stash->{message} = _("I'm afraid we couldn't validate that token. If you've copied the URL from an email, please check that you copied it exactly.\n");
+
+ my $auth_token = $c->forward( 'load_auth_token', [ $token_code, 'questionnaire' ] );
+ $c->stash->{id} = $auth_token->data;
+ $c->stash->{token} = $token_code;
+}
+
+sub questionnaire : Path('/Q') : Args(1) {
+ my ( $self, $c, $token_code ) = @_;
+ $c->forward( 'load_questionnaire_id', [ $token_code ] );
+ $c->forward( '/questionnaire/index');
+}
+
+=head2 load_auth_token
+
+ my $auth_token =
+ $c->forward( 'load_auth_token', [ $token_code, $token_scope ] );
+
+
+Load the token if possible. If token is not found, or not valid detach to a nice
+error message.
+
+=cut
+
+sub load_auth_token : Private {
+ my ( $self, $c, $token_code, $scope ) = @_;
+
+ # clean the token of bad chars (in case of email client issues)
+ $token_code ||= '';
+ $token_code =~ s{[^a-zA-Z0-9]+}{}g;
+
+ # try to load the token
+ my $token = $c->model('DB::Token')->find(
+ {
+ scope => $scope,
+ token => $token_code,
+ }
+ ) || $c->detach('token_error');
+
+ return $token;
+}
+
+=head2 token_error
+
+Display an error page saying that there is something wrong with the token.
+
+=cut
+
+sub token_error : Private {
+ my ( $self, $c ) = @_;
+ $c->stash->{template} = $c->stash->{error_template} || 'tokens/error.html';
+ $c->detach;
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Model/DB.pm b/perllib/FixMyStreet/App/Model/DB.pm
new file mode 100644
index 000000000..f9e43172f
--- /dev/null
+++ b/perllib/FixMyStreet/App/Model/DB.pm
@@ -0,0 +1,24 @@
+package FixMyStreet::App::Model::DB;
+use base 'Catalyst::Model::DBIC::Schema';
+
+use strict;
+use warnings;
+
+use FixMyStreet;
+
+__PACKAGE__->config(
+ schema_class => 'FixMyStreet::DB',
+ connect_info => FixMyStreet->dbic_connect_info,
+);
+
+=head1 NAME
+
+FixMyStreet::App::Model::DB - Catalyst DBIC Schema Model
+
+=head1 DESCRIPTION
+
+L<Catalyst::Model::DBIC::Schema> Model using schema L<FixMyStreet::DB>
+
+=cut
+
+1;
diff --git a/perllib/FixMyStreet/App/Model/EmailSend.pm b/perllib/FixMyStreet/App/Model/EmailSend.pm
new file mode 100644
index 000000000..73086c65f
--- /dev/null
+++ b/perllib/FixMyStreet/App/Model/EmailSend.pm
@@ -0,0 +1,51 @@
+package FixMyStreet::App::Model::EmailSend;
+use base 'Catalyst::Model::Adaptor';
+
+use strict;
+use warnings;
+
+use FixMyStreet;
+use Email::Send;
+
+=head1 NAME
+
+FixMyStreet::App::Model::EmailSend
+
+=head1 DESCRIPTION
+
+Thin wrapper around Email::Send - configuring it correctly acording to our config.
+
+If the config value 'SMTP_SMARTHOST' is set then email is routed via SMTP to
+that. Otherwise it is sent using a 'sendmail' like binary on the local system.
+
+And finally if if FixMyStreet->test_mode returns true then emails are not sent
+at all but are stored in memory for the test suite to inspect (using
+Email::Send::Test).
+
+=cut
+
+my $args = undef;
+
+if ( FixMyStreet->test_mode ) {
+
+ # Email::Send::Test
+ $args = { mailer => 'Test', };
+}
+elsif ( my $smtp_host = FixMyStreet->config('SMTP_SMARTHOST') ) {
+
+ # Email::Send::SMTP
+ $args = {
+ mailer => 'SMTP',
+ mailer_args => [ Host => $smtp_host ],
+ };
+}
+else {
+
+ # Email::Send::Sendmail
+ $args = { mailer => 'Sendmail' };
+}
+
+__PACKAGE__->config(
+ class => 'Email::Send',
+ args => $args,
+);
diff --git a/perllib/FixMyStreet/App/View/Email.pm b/perllib/FixMyStreet/App/View/Email.pm
new file mode 100644
index 000000000..86d5c1d60
--- /dev/null
+++ b/perllib/FixMyStreet/App/View/Email.pm
@@ -0,0 +1,44 @@
+package FixMyStreet::App::View::Email;
+use base 'Catalyst::View::TT';
+
+use strict;
+use warnings;
+
+use mySociety::Locale;
+use FixMyStreet;
+
+__PACKAGE__->config(
+ TEMPLATE_EXTENSION => '.txt',
+ INCLUDE_PATH => [ #
+ FixMyStreet->path_to( 'templates', 'email', 'default' ),
+ ],
+ ENCODING => 'utf8',
+ render_die => 1,
+ expose_methods => ['loc'],
+);
+
+=head1 NAME
+
+FixMyStreet::App::View::Email - TT View for FixMyStreet::App
+
+=head1 DESCRIPTION
+
+TT View for FixMyStreet::App.
+
+=cut
+
+=head2 loc
+
+ [% loc('Some text to localize') %]
+
+Passes the text to the localisation engine for translations.
+
+=cut
+
+sub loc {
+ my ( $self, $c, @args ) = @_;
+ return _(@args);
+}
+
+1;
+
diff --git a/perllib/FixMyStreet/App/View/Web.pm b/perllib/FixMyStreet/App/View/Web.pm
new file mode 100644
index 000000000..358e280c3
--- /dev/null
+++ b/perllib/FixMyStreet/App/View/Web.pm
@@ -0,0 +1,124 @@
+package FixMyStreet::App::View::Web;
+use base 'Catalyst::View::TT';
+
+use strict;
+use warnings;
+
+use mySociety::Locale;
+use mySociety::Web qw(ent);
+use FixMyStreet;
+use CrossSell;
+use Utils;
+
+__PACKAGE__->config(
+ TEMPLATE_EXTENSION => '.html',
+ INCLUDE_PATH => [ #
+ FixMyStreet->path_to( 'templates', 'web', 'default' ),
+ ],
+ ENCODING => 'utf8',
+ render_die => 1,
+ expose_methods => [
+ 'loc', 'nget', 'tprintf', 'display_crosssell_advert', 'prettify_epoch',
+ 'add_links',
+ ],
+);
+
+=head1 NAME
+
+FixMyStreet::App::View::Web - TT View for FixMyStreet::App
+
+=head1 DESCRIPTION
+
+TT View for FixMyStreet::App.
+
+=cut
+
+=head2 loc
+
+ [% loc('Some text to localize') %]
+
+Passes the text to the localisation engine for translations.
+
+=cut
+
+sub loc {
+ my ( $self, $c, @args ) = @_;
+ return _(@args);
+}
+
+=head2 nget
+
+ [% nget( 'singular', 'plural', $number ) %]
+
+Use first or second srting depending on the number.
+
+=cut
+
+sub nget {
+ my ( $self, $c, @args ) = @_;
+ return mySociety::Locale::nget(@args);
+}
+
+=head2 tprintf
+
+ [% tprintf( 'foo %s bar', 'insert' ) %]
+
+sprintf (different name to avoid clash)
+
+=cut
+
+sub tprintf {
+ my ( $self, $c, $format, @args ) = @_;
+ return sprintf $format, @args;
+}
+
+=head2 display_crosssell_advert
+
+ [% display_crosssell_advert( email, name ) %]
+
+Displays a crosssell advert if permitted by the cobrand.
+
+=cut
+
+sub display_crosssell_advert {
+ my ( $self, $c, $email, $name, %data ) = @_;
+
+ return unless $c->cobrand->allow_crosssell_adverts();
+ return CrossSell::display_advert( $c, $email, $name, %data );
+}
+
+=head2 Utils::prettify_epoch
+
+ [% pretty = prettify_epoch( $epoch, $short_bool ) %]
+
+Return a pretty version of the epoch.
+
+ $short_bool = 1; # 16:02, 29 Mar 2011
+ $short_bool = 0; # 16:02, Tuesday 29 March 2011
+
+=cut
+
+sub prettify_epoch {
+ my ( $self, $c, $epoch, $short_bool ) = @_;
+ return Utils::prettify_epoch( $epoch, $short_bool );
+}
+
+=head2 add_links
+
+ [% add_links( text ) | html_para %]
+
+Add some links to some text (and thus HTML-escapes the other text.
+
+=cut
+
+sub add_links {
+ my ( $self, $c, $text ) = @_;
+
+ $text =~ s/\r//g;
+ $text = ent($text);
+ $text =~ s{(https?://[^\s]+)}{<a href="$1">$1</a>}g;
+ return $text;
+}
+
+1;
+
diff --git a/perllib/FixMyStreet/Cobrand.pm b/perllib/FixMyStreet/Cobrand.pm
new file mode 100644
index 000000000..6fe2a2bc8
--- /dev/null
+++ b/perllib/FixMyStreet/Cobrand.pm
@@ -0,0 +1,91 @@
+# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
+# Email: evdb@mysociety.org. WWW: http://www.mysociety.org
+
+package FixMyStreet::Cobrand;
+
+use strict;
+use warnings;
+
+use FixMyStreet;
+use Carp;
+
+use Module::Pluggable
+ sub_name => '_cobrands',
+ search_path => ['FixMyStreet::Cobrand'],
+ require => 1;
+
+my @ALL_COBRAND_CLASSES = __PACKAGE__->_cobrands;
+
+=head2 get_allowed_cobrands
+
+Return an array reference of allowed cobrand subdomains
+
+=cut
+
+sub get_allowed_cobrands {
+ my $allowed_cobrand_string = FixMyStreet->config('ALLOWED_COBRANDS');
+ my @allowed_cobrands = split( /\|/, $allowed_cobrand_string );
+ return \@allowed_cobrands;
+}
+
+=head2 available_cobrand_classes
+
+ @available_cobrand_classes =
+ FixMyStreet::Cobrand->available_cobrand_classes();
+
+Return an array of all the classes that were found and that have monikers that
+match the values from get_allowed_cobrands.
+
+=cut
+
+sub available_cobrand_classes {
+ my $class = shift;
+
+ my %allowed = map { $_ => 1 } @{ $class->get_allowed_cobrands };
+ my @avail = grep { $allowed{ $_->moniker } } @ALL_COBRAND_CLASSES;
+
+ return @avail;
+}
+
+=head2 get_class_for_host
+
+ $cobrand_class = FixMyStreet::Cobrand->get_class_for_host( $host );
+
+Given a host determine which cobrand we should be using.
+
+=cut
+
+sub get_class_for_host {
+ my $class = shift;
+ my $host = shift;
+
+ foreach my $avail ( $class->available_cobrand_classes ) {
+ my $moniker = $avail->moniker;
+ return $avail if $host =~ m{$moniker};
+ }
+
+ # if none match then use the default
+ return 'FixMyStreet::Cobrand::Default';
+}
+
+=head2 get_class_for_moniker
+
+ $cobrand_class = FixMyStreet::Cobrand->get_class_for_moniker( $moniker );
+
+Given a moniker determine which cobrand we should be using.
+
+=cut
+
+sub get_class_for_moniker {
+ my $class = shift;
+ my $moniker = shift;
+
+ foreach my $avail ( $class->available_cobrand_classes ) {
+ return $avail if $moniker eq $avail->moniker;
+ }
+
+ # if none match then use the default
+ return 'FixMyStreet::Cobrand::Default';
+}
+
+1;
diff --git a/perllib/FixMyStreet/Cobrand/Barnet.pm b/perllib/FixMyStreet/Cobrand/Barnet.pm
new file mode 100644
index 000000000..bccd27885
--- /dev/null
+++ b/perllib/FixMyStreet/Cobrand/Barnet.pm
@@ -0,0 +1,82 @@
+package FixMyStreet::Cobrand::Barnet;
+use base 'FixMyStreet::Cobrand::Default';
+
+use strict;
+use warnings;
+
+use Carp;
+use URI::Escape;
+use mySociety::VotingArea;
+
+sub site_restriction {
+ return ( "and council='2489'", 'barnet', { council => '2489' } );
+}
+
+sub problems_clause {
+ return { council => '2489' };
+}
+
+sub problems {
+ my $self = shift;
+ return $self->{c}->model('DB::Problem')->search( $self->problems_clause );
+}
+
+sub base_url {
+ my $base_url = mySociety::Config::get('BASE_URL');
+ if ( $base_url !~ /barnet/ ) {
+ $base_url =~ s{http://(?!www\.)}{http://barnet.}g;
+ $base_url =~ s{http://www\.}{http://barnet.}g;
+ }
+ return $base_url;
+}
+
+sub site_title {
+ my ($self) = @_;
+ return 'Barnet Council FixMyStreet';
+}
+
+sub enter_postcode_text {
+ my ($self) = @_;
+ return 'Enter a Barnet postcode, or street name and area';
+}
+
+sub council_check {
+ my ( $self, $params, $context ) = @_;
+
+ my $councils = $params->{all_councils};
+ my $council_match = defined $councils->{2489};
+ if ($council_match) {
+ return 1;
+ }
+ my $url = 'http://www.fixmystreet.com/';
+ $url .= 'alert' if $context eq 'alert';
+ $url .= '?pc=' . URI::Escape::uri_escape( $self->{c}->req->param('pc') )
+ if $self->{c}->req->param('pc');
+ my $error_msg = "That location is not covered by Barnet.
+Please visit <a href=\"$url\">the main FixMyStreet site</a>.";
+ return ( 0, $error_msg );
+}
+
+# All reports page only has the one council.
+sub all_councils_report {
+ return 0;
+}
+
+sub disambiguate_location {
+ my ( $self, $s ) = @_;
+ $s = "ll=51.612832,-0.218169&spn=0.0563,0.09&$s";
+ return $s;
+}
+
+sub recent_photos {
+ my ( $self, $num, $lat, $lon, $dist ) = @_;
+ $num = 2 if $num == 3;
+ return $self->problems->recent_photos( $num, $lat, $lon, $dist );
+}
+
+sub tilma_mid_point {
+ return 189;
+}
+
+1;
+
diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm
new file mode 100644
index 000000000..332a0052d
--- /dev/null
+++ b/perllib/FixMyStreet/Cobrand/Default.pm
@@ -0,0 +1,850 @@
+package FixMyStreet::Cobrand::Default;
+
+use strict;
+use warnings;
+use FixMyStreet;
+use URI;
+
+use Carp;
+use mySociety::MaPit;
+
+=head2 new
+
+ my $cobrand = $class->new;
+ my $cobrand = $class->new( { c => $c } );
+
+Create a new cobrand object, optionally setting the context.
+
+You probably shouldn't need to do this and should get the cobrand object via a
+method in L<FixMyStreet::Cobrand> instead.
+
+=cut
+
+sub new {
+ my $class = shift;
+ my $self = shift || {};
+ return bless $self, $class;
+}
+
+=head2 moniker
+
+ $moniker = $cobrand_class->moniker();
+
+Returns a moniker that can be used to identify this cobrand. By default this is
+the last part of the class name lowercased - eg 'F::C::SomeCobrand' becomes
+'somecobrand'.
+
+=cut
+
+sub moniker {
+ my $class = ref( $_[0] ) || $_[0]; # deal with object or class
+ my ($last_part) = $class =~ m{::(\w+)$};
+ return lc($last_part);
+}
+
+=head2 is_default
+
+ $bool = $cobrand->is_default();
+
+Returns true if this is the default cobrand, false otherwise.
+
+=cut
+
+sub is_default {
+ my $self = shift;
+ return $self->moniker eq 'default';
+}
+
+=head2 path_to_web_templates
+
+ $path = $cobrand->path_to_web_templates( );
+
+Returns the path to the templates for this cobrand - by default
+"templates/web/$moniker"
+
+=cut
+
+sub path_to_web_templates {
+ my $self = shift;
+ return FixMyStreet->path_to( 'templates/web', $self->moniker );
+}
+
+=head1 problems_clause
+
+Returns a hash for a query to be used by problems (and elsewhere in joined
+queries) to restrict results for a cobrand.
+
+=cut
+
+sub problems_clause {}
+
+=head1 problems
+
+Returns a ResultSet of Problems, restricted to a subset if we're on a cobrand
+that only wants some of the data.
+
+=cut
+
+sub problems {
+ my $self = shift;
+ return $self->{c}->model('DB::Problem');
+}
+
+=head1 site_restriction
+
+Return a site restriction clause and a site key if the cobrand uses a subset of
+the FixMyStreet data. Parameter is any extra data the cobrand needs. Returns an
+empty string and site key 0 if the cobrand uses all the data.
+
+=cut
+
+sub site_restriction { return ( "", 0, {} ) }
+
+=head2 contact_restriction
+
+Return a contact restriction clause if the cobrand uses a subset of the
+FixMyStreet contact data.
+
+=cut
+
+sub contact_restriction {
+ {};
+}
+
+=head2 base_url_for_emails
+
+Return the base url to use in links in emails for the cobranded version of the
+site, parameter is extra data.
+
+=cut
+
+sub base_url_for_emails {
+ my $self = shift;
+ return $self->base_url;
+}
+
+=head2 base_url_with_lang
+
+=cut
+
+sub base_url_with_lang {
+ my $self = shift;
+ my $email = shift;
+
+ if ($email) {
+ return $self->base_url_for_emails;
+ } else {
+ return $self->base_url;
+ }
+}
+
+=head2 admin_base_url
+
+Base URL for the admin interface.
+
+=cut
+
+sub admin_base_url { 0 }
+
+=head2 writetothem_url
+
+URL for writetothem; parameter is COBRAND_DATA.
+
+=cut
+
+sub writetothem_url { 0 }
+
+=head2 base_url
+
+Return the base url for the cobranded version of the site
+
+=cut
+
+sub base_url { mySociety::Config::get('BASE_URL') }
+
+=head2 base_host
+
+Return the base host for the cobranded version of the site
+
+=cut
+
+sub base_host {
+ my $self = shift;
+ my $uri = URI->new( $self->base_url );
+ return $uri->host;
+}
+
+=head2 enter_postcode_text
+
+Return the text that prompts the user to enter their postcode/place name.
+Parameter is QUERY
+
+=cut
+
+sub enter_postcode_text { '' }
+
+=head2 set_lang_and_domain
+
+ my $set_lang = $cobrand->set_lang_and_domain( $lang, $unicode, $dir )
+
+Set the language and domain of the site based on the cobrand and host.
+
+=cut
+
+sub set_lang_and_domain {
+ my ( $self, $lang, $unicode, $dir ) = @_;
+ my $set_lang = mySociety::Locale::negotiate_language(
+ 'en-gb,English,en_GB', $lang
+ );
+ mySociety::Locale::gettext_domain( 'FixMyStreet', $unicode, $dir );
+ mySociety::Locale::change();
+ return $set_lang;
+}
+
+=head2 alert_list_options
+
+Return HTML for a list of alert options for the cobrand, given QUERY and
+OPTIONS.
+
+=cut
+
+sub alert_list_options { 0 }
+
+=head2 recent_photos
+
+Return N recent photos. If EASTING, NORTHING and DISTANCE are supplied, the
+photos must be attached to problems within DISTANCE of the point defined by
+EASTING and NORTHING.
+
+=cut
+
+sub recent_photos {
+ my $self = shift;
+ return $self->problems->recent_photos(@_);
+}
+
+=head2 recent
+
+Return recent problems on the site.
+
+=cut
+
+sub recent {
+ my ( $self ) = @_;
+ return $self->problems->recent();
+}
+
+=item shorten_recency_if_new_greater_than_fixed
+
+By default we want to shorten the recency so that the numbers are more
+attractive.
+
+=cut
+
+sub shorten_recency_if_new_greater_than_fixed {
+ return 1;
+}
+
+=head2 front_stats_data
+
+Return a data structure containing the front stats information that a template
+can then format.
+
+=cut
+
+sub front_stats_data {
+ my ( $self ) = @_;
+
+ my $recency = '1 week';
+ my $shorter_recency = '3 days';
+
+ my $fixed = $self->problems->recent_fixed();
+ my $updates = $self->problems->number_comments();
+ my $new = $self->problems->recent_new( $recency );
+
+ if ( $new > $fixed && $self->shorten_recency_if_new_greater_than_fixed ) {
+ $recency = $shorter_recency;
+ $new = $self->problems->recent_new( $recency );
+ }
+
+ my $stats = {
+ fixed => $fixed,
+ updates => $updates,
+ new => $new,
+ recency => $recency,
+ };
+
+ return $stats;
+}
+
+=head2 disambiguate_location
+
+Given a STRING ($_[1]) representing a location and a QUERY, return a string that
+includes any disambiguating information available
+
+=cut
+
+sub disambiguate_location { "$_[1]&gl=uk" }
+
+=head2 form_elements
+
+Parameters are FORM_NAME, QUERY. Return HTML for any extra needed elements for
+FORM_NAME
+
+=cut
+
+sub form_elements { '' }
+
+=head2 cobrand_data_for_generic_update
+
+Parameter is UPDATE_DATA, a reference to a hash of non-cobranded update data.
+Return cobrand extra data for the update
+
+=cut
+
+sub cobrand_data_for_generic_update { '' }
+
+=head2 cobrand_data_for_generic_update
+
+Parameter is PROBLEM_DATA, a reference to a hash of non-cobranded problem data.
+Return cobrand extra data for the problem
+
+=cut
+
+sub cobrand_data_for_generic_problem { '' }
+
+=head2 extra_problem_data
+
+Parameter is QUERY. Return a string of extra data to be stored with a problem
+
+=cut
+
+sub extra_problem_data { '' }
+
+=head2 extra_update_data
+
+Parameter is QUERY. Return a string of extra data to be stored with an update
+
+=cut
+
+sub extra_update_data { '' }
+
+=head2 extra_alert_data
+
+Parameter is QUERY. Return a string of extra data to be stored with an alert
+
+=cut
+
+sub extra_alert_data { '' }
+
+=head2 extra_data
+
+Given a QUERY, extract any extra data required by the cobrand
+
+=cut
+
+sub extra_data { '' }
+
+=head2 extra_problem_meta_text
+
+Returns any extra text to be displayed with a PROBLEM.
+
+=cut
+
+sub extra_problem_meta_text { '' }
+
+=head2 extra_update_meta_text
+
+Returns any extra text to be displayed with an UPDATE.
+
+=cut
+
+sub extra_update_meta_text { '' }
+
+=head2 uri
+
+Given a URL ($_[1]), QUERY, EXTRA_DATA, return a URL with any extra params
+needed appended to it.
+
+In the default case, if we're using an OpenLayers map, we need to make
+sure zoom is always present if lat/lon are, to stop OpenLayers defaulting
+to null/0.
+
+=cut
+
+sub uri {
+ my ( $self, $uri ) = @_;
+
+ (my $map_class = $FixMyStreet::Map::map_class) =~ s/^FixMyStreet::Map:://;
+ return $uri unless $map_class =~ /OSM|FMS/;
+
+ $uri = URI->new( $uri );
+ $uri->query_param( zoom => 3 )
+ if $uri->query_param('lat') && !$uri->query_param('zoom');
+ $uri->query_param( map => $map_class ); # FIXME Only on /around, /report?
+
+ return $uri;
+}
+
+
+=head2 header_params
+
+Return any params to be added to responses
+
+=cut
+
+sub header_params { return {} }
+
+=head2 root_path_js
+
+Parameter is QUERY. Return some js to set the root path from which AJAX queries
+should be made.
+
+=cut
+
+sub root_path_js { 'var root_path = "";' }
+
+=head2 site_title
+
+Return the title to be used in page heads.
+
+=cut
+
+sub site_title { 'FixMyStreet' }
+
+=head2 on_map_list_limit
+
+Return the maximum number of items to be given in the list of reports on the map
+
+=cut
+
+sub on_map_list_limit { return undef; }
+
+=head2 on_map_default_max_pin_age
+
+Return the default maximum age for pins.
+
+=cut
+
+sub on_map_default_max_pin_age { return '6 months'; }
+
+=head2 allow_photo_upload
+
+Return a boolean indicating whether the cobrand allows photo uploads
+
+=cut
+
+sub allow_photo_upload { return 1; }
+
+=head2 allow_crosssell_adverts
+
+Return a boolean indicating whether the cobrand allows the display of crosssell
+adverts
+
+=cut
+
+sub allow_crosssell_adverts { return 1; }
+
+=head2 allow_photo_display
+
+Return a boolean indicating whether the cobrand allows photo display
+
+=cut
+
+sub allow_photo_display { return 1; }
+
+=head2 allow_update_reporting
+
+Return a boolean indication whether users should see links next to updates
+allowing them to report them as offensive.
+
+=cut
+
+sub allow_update_reporting { return 0; }
+
+=head2 geocoded_string_check
+
+Parameters are LOCATION, QUERY. Return a boolean indicating whether the
+string LOCATION passes the cobrands checks.
+
+=cut
+
+sub geocoded_string_check { return 1; }
+
+=head2 council_check
+
+Paramters are COUNCILS, QUERY, CONTEXT. Return a boolean indicating whether
+COUNCILS pass any extra checks. CONTEXT is where we are on the site.
+
+=cut
+
+sub council_check { return ( 1, '' ); }
+
+=head2 feed_xsl
+
+Return an XSL to be used in rendering feeds
+
+=cut
+
+sub feed_xsl { '/xsl.xsl' }
+
+=head2 all_councils_report
+
+Return a boolean indicating whether the cobrand displays a report of all
+councils
+
+=cut
+
+sub all_councils_report { 1 }
+
+=head2 ask_ever_reported
+
+Return a boolean indicating whether people should be asked whether this is the
+first time they' ve reported a problem
+
+=cut
+
+sub ask_ever_reported { 1 }
+
+=head2 admin_pages
+
+List of names of pages to display on the admin interface
+
+=cut
+
+sub admin_pages { 0 }
+
+=head2 admin_show_creation_graph
+
+Show the problem creation graph in the admin interface
+=cut
+
+sub admin_show_creation_graph { 1 }
+
+=head2 area_types, area_min_generation
+
+The MaPit types this site handles
+
+=cut
+
+sub area_types { return qw(DIS LBO MTD UTA CTY COI); }
+sub area_min_generation { 10 }
+
+=head2 tilma_mid_point
+
+If the map is smaller than the usual, return half its size.
+
+=cut
+
+sub tilma_mid_point {
+ return 0;
+}
+
+=head2 contact_name, contact_email
+
+Return the contact name or email for the cobranded version of the site (to be
+used in emails).
+
+=cut
+
+sub contact_name { $_[0]->get_cobrand_conf('CONTACT_NAME') }
+sub contact_email { $_[0]->get_cobrand_conf('CONTACT_EMAIL') }
+
+=head2 get_cobrand_conf COBRAND KEY
+
+Get the value for KEY from the config file for COBRAND
+
+=cut
+
+sub get_cobrand_conf {
+ my ( $self, $key ) = @_;
+ my $value = undef;
+ my $cobrand_moniker = $self->moniker;
+
+ my $cobrand_config_file =
+ FixMyStreet->path_to("conf/cobrands/$cobrand_moniker/general");
+ my $normal_config_file = FixMyStreet->path_to('conf/general');
+
+ if ( -e $cobrand_config_file ) {
+
+ # FIXME - don't rely on the config file name - should
+ # change mySociety::Config so that it can return values from a
+ # particular config file instead
+ mySociety::Config::set_file("$cobrand_config_file");
+ my $config_key = $key . "_" . uc($cobrand_moniker);
+ $value = mySociety::Config::get( $config_key, undef );
+ mySociety::Config::set_file("$normal_config_file");
+ }
+
+ # If we didn't find a value use one from normal config
+ if ( !defined($value) ) {
+ $value = mySociety::Config::get($key);
+ }
+
+ return $value;
+}
+
+=item email_host
+
+Return if we are the virtual host that sends email for this cobrand
+
+=cut
+
+sub email_host {
+ my $self = shift;
+ my $cobrand_moniker_uc = uc( $self->moniker );
+
+ my $email_vhost =
+ mySociety::Config::get("EMAIL_VHOST_$cobrand_moniker_uc", '')
+ || mySociety::Config::get("EMAIL_VHOST")
+ || '';
+
+ return $email_vhost
+ && "http://$email_vhost" eq mySociety::Config::get("BASE_URL");
+}
+
+=item remove_redundant_councils
+
+Remove councils whose reports go to another council
+
+=cut
+
+sub remove_redundant_councils {
+ my $self = shift;
+ my $all_councils = shift;
+
+ # Ipswich & St Edmundsbury are responsible for everything in their
+ # areas, not Suffolk
+ delete $all_councils->{2241}
+ if $all_councils->{2446} #
+ || $all_councils->{2443};
+
+ # Norwich is responsible for everything in its areas, not Norfolk
+ delete $all_councils->{2233} #
+ if $all_councils->{2391};
+}
+
+=item filter_all_council_ids_list
+
+Removes any council IDs that we don't need from an array and returns the
+filtered array
+
+=cut
+
+sub filter_all_council_ids_list {
+ my $self = shift;
+ return @_;
+}
+
+=item short_name
+
+Remove extra information from council names for tidy URIs
+
+=cut
+
+sub short_name {
+ my $self = shift;
+ my ($area, $info) = @_;
+ # Special case Durham as it's the only place with two councils of the same name
+ return 'Durham+County' if $area->{name} eq 'Durham County Council';
+ return 'Durham+City' if $area->{name} eq 'Durham City Council';
+
+ my $name = $area->{name};
+ $name =~ s/ (Borough|City|District|County) Council$//;
+ $name =~ s/ Council$//;
+ $name =~ s/ & / and /;
+ $name = URI::Escape::uri_escape_utf8($name);
+ $name =~ s/%20/+/g;
+ return $name;
+
+}
+
+=item council_rss_alert_options
+
+Generate a set of options for council rss alerts.
+
+=cut
+
+sub council_rss_alert_options {
+ my $self = shift;
+ my $all_councils = shift;
+
+ my %councils = map { $_ => 1 } $self->area_types();
+
+ my $num_councils = scalar keys %$all_councils;
+
+ my ( @options, @reported_to_options );
+ if ( $num_councils == 1 or $num_councils == 2 ) {
+ my ($council, $ward);
+ foreach (values %$all_councils) {
+ if ($councils{$_->{type}}) {
+ $council = $_;
+ $council->{short_name} = $self->short_name( $council );
+ ( $council->{id_name} = $council->{short_name} ) =~ tr/+/_/;
+ } else {
+ $ward = $_;
+ $ward->{short_name} = $self->short_name( $ward );
+ ( $ward->{id_name} = $ward->{short_name} ) =~ tr/+/_/;
+ }
+ }
+
+ push @options,
+ {
+ type => 'council',
+ id => sprintf( 'council:%s:%s', $council->{id}, $council->{id_name} ),
+ text => sprintf( _('Problems within %s'), $council->{name}),
+ rss_text => sprintf( _('RSS feed of problems within %s'), $council->{name}),
+ uri => $self->uri( '/rss/reports/' . $council->{short_name} ),
+ };
+ push @options,
+ {
+ type => 'ward',
+ id => sprintf( 'ward:%s:%s:%s:%s', $council->{id}, $ward->{id}, $council->{id_name}, $ward->{id_name} ),
+ rss_text => sprintf( _('RSS feed of problems within %s ward'), $ward->{name}),
+ text => sprintf( _('Problems within %s ward'), $ward->{name}),
+ uri => $self->uri( '/rss/reports/' . $council->{short_name} . '/' . $ward->{short_name} ),
+ } if $ward;
+ } elsif ( $num_councils == 4 ) {
+# # Two-tier council
+ my ($county, $district, $c_ward, $d_ward);
+ foreach (values %$all_councils) {
+ $_->{short_name} = $self->short_name( $_ );
+ ( $_->{id_name} = $_->{short_name} ) =~ tr/+/_/;
+ if ($_->{type} eq 'CTY') {
+ $county = $_;
+ } elsif ($_->{type} eq 'DIS') {
+ $district = $_;
+ } elsif ($_->{type} eq 'CED') {
+ $c_ward = $_;
+ } elsif ($_->{type} eq 'DIW') {
+ $d_ward = $_;
+ }
+ }
+ my $district_name = $district->{name};
+ my $d_ward_name = $d_ward->{name};
+ my $county_name = $county->{name};
+ my $c_ward_name = $c_ward->{name};
+
+ push @options,
+ {
+ type => 'area',
+ id => sprintf( 'area:%s:%s', $district->{id}, $district->{id_name} ),
+ text => $district_name,
+ rss_text => sprintf( _('RSS feed for %s'), $district_name ),
+ uri => $self->uri( '/rss/areas/' . $district->{short_name} )
+ },
+ {
+ type => 'area',
+ id => sprintf( 'area:%s:%s:%s:%s', $district->{id}, $d_ward->{id}, $district->{id_name}, $d_ward->{id_name} ),
+ text => sprintf( _('%s ward, %s'), $d_ward_name, $district_name ),
+ rss_text => sprintf( _('RSS feed for %s ward, %s'), $d_ward_name, $district_name ),
+ uri => $self->uri( '/rss/areas/' . $district->{short_name} . '/' . $d_ward->{short_name} )
+ },
+ {
+ type => 'area',
+ id => sprintf( 'area:%s:%s', $county->{id}, $county->{id_name} ),
+ text => $county_name,
+ rss_text => sprintf( _('RSS feed for %s'), $county_name ),
+ uri => $self->uri( '/rss/areas/' . $county->{short_name} )
+ },
+ {
+ type => 'area',
+ id => sprintf( 'area:%s:%s:%s:%s', $county->{id}, $c_ward->{id}, $county->{id_name}, $c_ward->{id_name} ),
+ text => sprintf( _('%s ward, %s'), $c_ward_name, $county_name ),
+ rss_text => sprintf( _('RSS feed for %s ward, %s'), $c_ward_name, $county_name ),
+ uri => $self->uri( '/rss/areas/' . $county->{short_name} . '/' . $c_ward->{short_name} )
+ };
+
+ push @reported_to_options,
+ {
+ type => 'council',
+ id => sprintf( 'council:%s:%s', $district->{id}, $district->{id_name} ),
+ text => $district->{name},
+ rss_text => sprintf( _('RSS feed of %s'), $district->{name}),
+ uri => $self->uri( '/rss/reports/' . $district->{short_name} ),
+ },
+ {
+ type => 'ward',
+ id => sprintf( 'ward:%s:%s:%s:%s', $district->{id}, $d_ward->{id}, $district->{id_name}, $d_ward->{id_name} ),
+ rss_text => sprintf( _('RSS feed of %s, within %s ward'), $district->{name}, $d_ward->{name}),
+ text => sprintf( _('%s, within %s ward'), $district->{name}, $d_ward->{name}),
+ uri => $self->uri( '/rss/reports/' . $district->{short_name} . '/' . $d_ward->{short_name} ),
+ },
+ {
+ type => 'council',
+ id => sprintf( 'council:%s:%s', $county->{id}, $county->{id_name} ),
+ text => $county->{name},
+ rss_text => sprintf( _('RSS feed of %s'), $county->{name}),
+ uri => $self->uri( '/rss/reports/' . $county->{short_name} ),
+ },
+ {
+ type => 'ward',
+ id => sprintf( 'ward:%s:%s:%s:%s', $county->{id}, $c_ward->{id}, $county->{id_name}, $c_ward->{id_name} ),
+ rss_text => sprintf( _('RSS feed of %s, within %s ward'), $county->{name}, $c_ward->{name}),
+ text => sprintf( _('%s, within %s ward'), $county->{name}, $c_ward->{name}),
+ uri => $self->uri( '/rss/reports/' . $county->{short_name} . '/' . $c_ward->{short_name} ),
+ };
+
+
+ } else {
+ throw Error::Simple('An area with three tiers of council? Impossible! '. join('|',keys %$all_councils));
+ }
+
+ return ( \@options, @reported_to_options ? \@reported_to_options : undef );
+}
+
+=head2 generate_problem_banner
+
+ my $banner = $c->cobrand->generate_problem_banner;
+
+ <p id="[% banner.id %]:>[% banner.text %]</p>
+
+Generate id and text for banner that appears at top of problem page.
+
+=cut
+
+sub generate_problem_banner {
+ my ( $self, $problem ) = @_;
+
+ my $banner = {};
+ if ($problem->state eq 'confirmed' && time() - $problem->lastupdate_local->epoch > 8*7*24*60*60) {
+ $banner->{id} = 'unknown';
+ $banner->{text} = _('This problem is old and of unknown status.');
+ }
+ if ($problem->state eq 'fixed') {
+ $banner->{id} = 'fixed';
+ $banner->{text} = _('This problem has been fixed') . '.';
+ }
+
+ return $banner;
+}
+
+sub reports_council_check {
+ my ( $self, $c, $code ) = @_;
+
+ if ($code =~ /^(\d\d)([a-z]{2})?([a-z]{2})?$/i) {
+ my $area = mySociety::MaPit::call( 'area', uc $code );
+ $c->detach( 'redirect_index' ) if $area->{error}; # Given a bad/old ONS code
+ if (length($code) == 6) {
+ my $council = mySociety::MaPit::call( 'area', $area->{parent_area} );
+ $c->stash->{ward} = $area;
+ $c->stash->{council} = $council;
+ $c->detach( 'redirect_ward' );
+ } else {
+ $c->stash->{council} = $area;
+ $c->detach( 'redirect_council' );
+ }
+ }
+}
+
+=head2 default_photo_resize
+
+Size that photos are to be resized to for display. If photos aren't
+to be resized then return 0;
+
+=cut
+
+sub default_photo_resize { return 0; }
+
+1;
+
diff --git a/perllib/FixMyStreet/Cobrand/EmptyHomes.pm b/perllib/FixMyStreet/Cobrand/EmptyHomes.pm
new file mode 100644
index 000000000..2199d660e
--- /dev/null
+++ b/perllib/FixMyStreet/Cobrand/EmptyHomes.pm
@@ -0,0 +1,177 @@
+package FixMyStreet::Cobrand::EmptyHomes;
+use base 'FixMyStreet::Cobrand::Default';
+
+use strict;
+use warnings;
+
+use FixMyStreet;
+use mySociety::Locale;
+use Carp;
+
+=item
+
+Return the base url for this cobranded site
+
+=cut
+
+sub base_url {
+ my $base_url = FixMyStreet->config('BASE_URL');
+ if ( $base_url !~ /emptyhomes/ ) {
+ $base_url =~ s/http:\/\//http:\/\/emptyhomes\./g;
+ }
+ return $base_url;
+}
+
+sub admin_base_url {
+ return 'https://secure.mysociety.org/admin/emptyhomes/';
+}
+
+sub area_types {
+ return qw(DIS LBO MTD UTA LGD COI); # No CTY
+}
+
+
+sub base_url_with_lang {
+ my $self = shift;
+ my $email = shift;
+
+ my $base = $self->base_url;
+
+ if ($email) {
+ $base = $self->base_url_for_emails;
+ }
+
+ my $lang = $mySociety::Locale::lang;
+ if ($lang eq 'cy') {
+ $base =~ s{http://}{$&cy.};
+ } else {
+ $base =~ s{http://}{$&en.};
+ }
+ return $base;
+}
+
+=item set_lang_and_domain LANG UNICODE
+
+Set the language and text domain for the site based on the query and host.
+
+=cut
+
+sub set_lang_and_domain {
+ my ( $self, $lang, $unicode, $dir ) = @_;
+ my $set_lang = mySociety::Locale::negotiate_language(
+ 'en-gb,English,en_GB|cy,Cymraeg,cy_GB', $lang );
+ mySociety::Locale::gettext_domain( 'FixMyStreet-EmptyHomes', $unicode,
+ $dir );
+ mySociety::Locale::change();
+ return $set_lang;
+}
+
+=item site_title
+
+Return the title to be used in page heads
+
+=cut
+
+sub site_title {
+ my ($self) = @_;
+ return _('Report Empty Homes');
+}
+
+=item feed_xsl
+
+Return the XSL file path to be used for feeds'
+
+=cut
+
+sub feed_xsl {
+ my ($self) = @_;
+ return '/xsl.eha.xsl';
+}
+
+=item shorten_recency_if_new_greater_than_fixed
+
+For empty homes we don't want to shorten the recency
+
+=cut
+
+sub shorten_recency_if_new_greater_than_fixed {
+ return 0;
+}
+
+=head2 generate_problem_banner
+
+ my $banner = $c->cobrand->generate_problem_banner;
+
+ <p id="[% banner.id %]:>[% banner.text %]</p>
+
+Generate id and text for banner that appears at top of problem page.
+
+=cut
+
+sub generate_problem_banner {
+ my ( $self, $problem ) = @_;
+
+ my $banner = {};
+ if ($problem->state eq 'fixed') {
+ $banner->{id} = 'fixed';
+ $banner->{text} = _('This problem has been fixed') . '.';
+ }
+
+ return $banner;
+}
+
+=head2 default_photo_resize
+
+Size that photos are to be resized to for display. If photos aren't
+to be resized then return 0;
+
+=cut
+
+sub default_photo_resize { return '195x'; }
+
+=item council_rss_alert_options
+
+Generate a set of options for council rss alerts.
+
+=cut
+
+sub council_rss_alert_options {
+ my $self = shift;
+ my $all_councils = shift;
+
+ my %councils = map { $_ => 1 } $self->area_types();
+
+ my $num_councils = scalar keys %$all_councils;
+
+ my ( @options, @reported_to_options );
+ my ($council, $ward);
+ foreach (values %$all_councils) {
+ $_->{short_name} = $self->short_name( $_ );
+ ( $_->{id_name} = $_->{short_name} ) =~ tr/+/_/;
+ if ($_->{type} eq 'DIS') {
+ $council = $_;
+ } elsif ($_->{type} eq 'DIW') {
+ $ward = $_;
+ }
+ }
+
+ push @options, {
+ type => 'council',
+ id => sprintf( 'council:%s:%s', $council->{id}, $council->{id_name} ),
+ text => sprintf( _('Problems within %s'), $council->{name}),
+ rss_text => sprintf( _('RSS feed of problems within %s'), $council->{name}),
+ uri => $self->uri( '/rss/reports/' . $council->{short_name} ),
+ };
+ push @options, {
+ type => 'ward',
+ id => sprintf( 'ward:%s:%s:%s:%s', $council->{id}, $ward->{id}, $council->{id_name}, $ward->{id_name} ),
+ rss_text => sprintf( _('RSS feed of problems within %s ward'), $ward->{name}),
+ text => sprintf( _('Problems within %s ward'), $ward->{name}),
+ uri => $self->uri( '/rss/reports/' . $council->{short_name} . '/' . $ward->{short_name} ),
+ };
+
+ return ( \@options, @reported_to_options ? \@reported_to_options : undef );
+}
+
+1;
+
diff --git a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm
new file mode 100644
index 000000000..eb7ea89a5
--- /dev/null
+++ b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm
@@ -0,0 +1,211 @@
+package FixMyStreet::Cobrand::FiksGataMi;
+use base 'FixMyStreet::Cobrand::Default';
+
+use strict;
+use warnings;
+
+use Carp;
+use mySociety::MaPit;
+
+sub set_lang_and_domain {
+ my ( $self, $lang, $unicode, $dir ) = @_;
+ mySociety::Locale::negotiate_language(
+ 'en-gb,English,en_GB|nb,Norwegian,nb_NO', 'nb' );
+ mySociety::Locale::gettext_domain( 'FixMyStreet', $unicode, $dir );
+ mySociety::Locale::change();
+}
+
+sub enter_postcode_text {
+ my ( $self ) = @_;
+ return _('Enter a nearby postcode, or street name and area');
+}
+
+# Is also adding language parameter
+sub disambiguate_location {
+ my ( $self, $s ) = @_;
+ $s = "hl=no&gl=no&$s";
+ return $s;
+}
+
+sub area_types {
+ return ( 'NKO', 'NFY' );
+}
+
+sub area_min_generation {
+ return '';
+}
+
+sub admin_base_url {
+ return 'http://www.fiksgatami.no/admin/';
+}
+
+sub writetothem_url {
+ return 'http://www.norge.no/styresmakter/';
+}
+
+# If lat/lon are present in the URL, OpenLayers will use that to centre the map.
+# Need to specify a zoom to stop it defaulting to null/0.
+sub uri {
+ my ( $self, $uri ) = @_;
+
+ $uri = URI->new( $uri );
+ $uri->query_param( zoom => 3 )
+ if $uri->query_param('lat') && !$uri->query_param('zoom');
+
+ return $uri;
+}
+
+sub geocoded_string_check {
+ my ( $self, $s ) = @_;
+ return 1 if $s =~ /, Norge/;
+ return 0;
+}
+
+sub remove_redundant_councils {
+ my $self = shift;
+ my $all_councils = shift;
+
+ # Oslo is both a kommune and a fylke, we only want to show it once
+ delete $all_councils->{301} #
+ if $all_councils->{3};
+}
+
+sub filter_all_council_ids_list {
+ my $self = shift;
+ my @all_councils_ids = @_;
+
+ # as above we only want to show Oslo once
+ return grep { $_ != 301 } @all_councils_ids;
+}
+
+sub short_name {
+ my $self = shift;
+ my ($area, $info) = @_;
+
+ if ($area->{name} =~ /^(Os|Nes|V\xe5ler|Sande|B\xf8|Her\xf8y)$/) {
+ my $parent = $info->{$area->{parent_area}}->{name};
+ return URI::Escape::uri_escape_utf8("$area->{name}, $parent");
+ }
+
+ my $name = $area->{name};
+ $name =~ s/ & / and /;
+ $name = URI::Escape::uri_escape_utf8($name);
+ $name =~ s/%20/+/g;
+ return $name;
+}
+
+sub council_rss_alert_options {
+ my $self = shift;
+ my $all_councils = shift;
+ my $c = shift;
+
+ my ( @options, @reported_to_options, $fylke, $kommune );
+
+ foreach ( values %$all_councils ) {
+ if ( $_->{type} eq 'NKO' ) {
+ $kommune = $_;
+ }
+ else {
+ $fylke = $_;
+ }
+ }
+
+ if ( $fylke->{id} == 3 ) { # Oslo
+ my $short_name = $self->short_name($fylke, $all_councils);
+ ( my $id_name = $short_name ) =~ tr/+/_/;
+
+ push @options,
+ {
+ type => 'council',
+ id => sprintf( 'council:%s:%s', $fylke->{id}, $id_name ),
+ rss_text =>
+ sprintf( _('RSS feed of problems within %s'), $fylke->{name} ),
+ text => sprintf( _('Problems within %s'), $fylke->{name} ),
+ uri => $c->uri_for( '/rss/reports', $short_name ),
+ };
+ }
+ else {
+ my $short_kommune_name = $self->short_name($kommune, $all_councils);
+ ( my $id_kommune_name = $short_kommune_name ) =~ tr/+/_/;
+
+ my $short_fylke_name = $self->short_name($fylke, $all_councils);
+ ( my $id_fylke_name = $short_fylke_name ) =~ tr/+/_/;
+
+ push @options,
+ {
+ type => 'area',
+ id => sprintf( 'area:%s:%s', $kommune->{id}, $id_kommune_name ),
+ rss_text =>
+ sprintf( _('RSS feed of %s'), $kommune->{name} ),
+ text => $kommune->{name},
+ uri => $c->uri_for( '/rss/area', $short_kommune_name ),
+ },
+ {
+ type => 'area',
+ id => sprintf( 'area:%s:%s', $fylke->{id}, $id_fylke_name ),
+ rss_text =>
+ sprintf( _('RSS feed of %s'), $fylke->{name} ),
+ text => $fylke->{name},
+ uri => $c->uri_for( '/rss/area', $short_fylke_name ),
+ };
+
+ push @reported_to_options,
+ {
+ type => 'council',
+ id => sprintf( 'council:%s:%s', $kommune->{id}, $id_kommune_name ),
+ rss_text =>
+ sprintf( _('RSS feed of %s'), $kommune->{name} ),
+ text => $kommune->{name},
+ uri => $c->uri_for( '/rss/reports', $short_kommune_name ),
+ },
+ {
+ type => 'council',
+ id => sprintf( 'council:%s:%s', $fylke->{id}, $id_fylke_name ),
+ rss_text =>
+ sprintf( _('RSS feed of %s'), $fylke->{name} ),
+ text => $fylke->{name},
+ uri => $c->uri_for( '/rss/reports/', $short_fylke_name ),
+ };
+ }
+
+ return (
+ \@options, @reported_to_options
+ ? \@reported_to_options
+ : undef
+ );
+
+}
+
+sub reports_council_check {
+ my ( $self, $c, $council ) = @_;
+
+ if ($council eq 'Oslo') {
+
+ # There are two Oslos (kommune and fylke), we only want one of them.
+ $c->stash->{council} = mySociety::MaPit::call('area', 3);
+ return 1;
+
+ } elsif ($council =~ /,/) {
+
+ # Some kommunes have the same name, use the fylke name to work out which.
+ my ($kommune, $fylke) = split /\s*,\s*/, $council;
+ my @area_types = $c->cobrand->area_types;
+ my $areas_k = mySociety::MaPit::call('areas', $kommune, type => \@area_types);
+ my $areas_f = mySociety::MaPit::call('areas', $fylke, type => \@area_types);
+ use Data::Dumper;
+ if (keys %$areas_f == 1) {
+ ($fylke) = values %$areas_f;
+ foreach (values %$areas_k) {
+ if ($_->{name} eq $kommune && $_->{parent_area} == $fylke->{id}) {
+ $c->stash->{council} = $_;
+ return 1;
+ }
+ }
+ }
+ # If we're here, we've been given a bad name.
+ $c->detach( 'redirect_index' );
+
+ }
+}
+
+1;
diff --git a/perllib/FixMyStreet/Cobrand/Southampton.pm b/perllib/FixMyStreet/Cobrand/Southampton.pm
new file mode 100644
index 000000000..6b4aae742
--- /dev/null
+++ b/perllib/FixMyStreet/Cobrand/Southampton.pm
@@ -0,0 +1,82 @@
+package FixMyStreet::Cobrand::Southampton;
+use base 'FixMyStreet::Cobrand::Default';
+
+use strict;
+use warnings;
+
+use Carp;
+use URI::Escape;
+use mySociety::VotingArea;
+
+sub site_restriction {
+ return ( "and council='2567'", 'southampton', { council => '2567' } );
+}
+
+sub problems_clause {
+ return { council => '2567' };
+}
+
+sub problems {
+ my $self = shift;
+ return $self->{c}->model('DB::Problem')->search( $self->problems_clause );
+}
+
+sub base_url {
+ my $base_url = mySociety::Config::get('BASE_URL');
+ if ($base_url !~ /southampton/) {
+ $base_url =~ s{http://(?!www\.)}{http://southampton.}g;
+ $base_url =~ s{http://www\.}{http://southampton.}g;
+ }
+ return $base_url;
+}
+
+sub site_title {
+ my ( $self ) = @_;
+ return 'Southampton City Council FixMyStreet';
+}
+
+sub enter_postcode_text {
+ my ( $self ) = @_;
+ return 'Enter a Southampton postcode, or street name and area';
+}
+
+sub council_check {
+ my ( $self, $params, $context ) = @_;
+
+ my $councils = $params->{all_councils};
+ my $council_match = defined $councils->{2567};
+ if ($council_match) {
+ return 1;
+ }
+ my $url = 'http://www.fixmystreet.com/';
+ $url .= 'alert' if $context eq 'alert';
+ $url .= '?pc=' . URI::Escape::uri_escape_utf8($self->{c}->req->param('pc'))
+ if $self->{c}->req->param('pc');
+ my $error_msg = "That location is not covered by Southampton.
+Please visit <a href=\"$url\">the main FixMyStreet site</a>.";
+ return ( 0, $error_msg );
+}
+
+# All reports page only has the one council.
+sub all_councils_report {
+ return 0;
+}
+
+sub disambiguate_location {
+ my ( $self, $s ) = @_;
+ $s = "ll=50.913822,-1.400493&spn=0.084628,0.15701&$s";
+ return $s;
+}
+
+sub recent_photos {
+ my ($self, $num, $lat, $lon, $dist) = @_;
+ $num = 2 if $num == 3;
+ return $self->problems->recent_photos( $num, $lat, $lon, $dist );
+}
+
+sub tilma_mid_point {
+ return 189;
+}
+
+1;
+
diff --git a/perllib/FixMyStreet/DB.pm b/perllib/FixMyStreet/DB.pm
new file mode 100644
index 000000000..18c8cc2ca
--- /dev/null
+++ b/perllib/FixMyStreet/DB.pm
@@ -0,0 +1,17 @@
+package FixMyStreet::DB;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Schema';
+
+__PACKAGE__->load_namespaces;
+
+# Created by DBIx::Class::Schema::Loader v0.07009 @ 2011-03-01 15:43:43
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:tJZ+CpaAfZVPrctDXTZTuQ
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
diff --git a/perllib/FixMyStreet/DB/Result/Abuse.pm b/perllib/FixMyStreet/DB/Result/Abuse.pm
new file mode 100644
index 000000000..df3ebe24a
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/Abuse.pm
@@ -0,0 +1,21 @@
+package FixMyStreet::DB::Result::Abuse;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime");
+__PACKAGE__->table("abuse");
+__PACKAGE__->add_columns("email", { data_type => "text", is_nullable => 0 });
+__PACKAGE__->set_primary_key("email");
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-05-24 15:32:43
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Bc0deuJiQlKyMGzLTUAIxg
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
diff --git a/perllib/FixMyStreet/DB/Result/AdminLog.pm b/perllib/FixMyStreet/DB/Result/AdminLog.pm
new file mode 100644
index 000000000..e57773e63
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/AdminLog.pm
@@ -0,0 +1,44 @@
+package FixMyStreet::DB::Result::AdminLog;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime");
+__PACKAGE__->table("admin_log");
+__PACKAGE__->add_columns(
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "admin_log_id_seq",
+ },
+ "admin_user",
+ { data_type => "text", is_nullable => 0 },
+ "object_type",
+ { data_type => "text", is_nullable => 0 },
+ "object_id",
+ { data_type => "integer", is_nullable => 0 },
+ "action",
+ { data_type => "text", is_nullable => 0 },
+ "whenedited",
+ {
+ data_type => "timestamp",
+ default_value => \"ms_current_timestamp()",
+ is_nullable => 0,
+ },
+);
+__PACKAGE__->set_primary_key("id");
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-06-06 18:52:09
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:VKN3o9SHoDhG2/H5NInf2w
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
diff --git a/perllib/FixMyStreet/DB/Result/Alert.pm b/perllib/FixMyStreet/DB/Result/Alert.pm
new file mode 100644
index 000000000..3a2dc9a9f
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/Alert.pm
@@ -0,0 +1,127 @@
+package FixMyStreet::DB::Result::Alert;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime");
+__PACKAGE__->table("alert");
+__PACKAGE__->add_columns(
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "alert_id_seq",
+ },
+ "alert_type",
+ { data_type => "text", is_foreign_key => 1, is_nullable => 0 },
+ "parameter",
+ { data_type => "text", is_nullable => 1 },
+ "parameter2",
+ { data_type => "text", is_nullable => 1 },
+ "confirmed",
+ { data_type => "integer", default_value => 0, is_nullable => 0 },
+ "lang",
+ { data_type => "text", default_value => "en-gb", is_nullable => 0 },
+ "cobrand",
+ { data_type => "text", default_value => "", is_nullable => 0 },
+ "cobrand_data",
+ { data_type => "text", default_value => "", is_nullable => 0 },
+ "whensubscribed",
+ {
+ data_type => "timestamp",
+ default_value => \"ms_current_timestamp()",
+ is_nullable => 0,
+ },
+ "whendisabled",
+ { data_type => "timestamp", is_nullable => 1 },
+ "user_id",
+ { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("id");
+__PACKAGE__->belongs_to(
+ "alert_type",
+ "FixMyStreet::DB::Result::AlertType",
+ { ref => "alert_type" },
+ { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+__PACKAGE__->belongs_to(
+ "user",
+ "FixMyStreet::DB::Result::User",
+ { id => "user_id" },
+ { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+__PACKAGE__->has_many(
+ "alert_sents",
+ "FixMyStreet::DB::Result::AlertSent",
+ { "foreign.alert_id" => "self.id" },
+ { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-06-03 16:48:36
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:NlyhWyg0NrH5/kZYYO36qg
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+use DateTime::TimeZone;
+use Moose;
+use namespace::clean -except => [ 'meta' ];
+
+with 'FixMyStreet::Roles::Abuser';
+
+my $tz = DateTime::TimeZone->new( name => "local" );
+
+
+sub whensubscribed_local {
+ my $self = shift;
+
+ return $self->whensubscribed
+ ? $self->whensubscribed->set_time_zone($tz)
+ : $self->whensubscribed;
+}
+
+sub whendisabled_local {
+ my $self = shift;
+
+ return $self->whendisabled
+ ? $self->whendisabled->set_time_zone($tz)
+ : $self->whendisabled;
+}
+
+=head2 confirm
+
+ $alert->confirm();
+
+Sets the state of the alert to confirmed.
+
+=cut
+
+sub confirm {
+ my $self = shift;
+
+ $self->confirmed(1);
+ $self->whendisabled(undef);
+ $self->update;
+
+ return 1;
+}
+
+sub disable {
+ my $self = shift;
+
+ $self->whendisabled( \'ms_current_timestamp()' );
+ $self->update;
+
+ return 1;
+}
+
+# need the inline_constuctor bit as we don't inherit from Moose
+__PACKAGE__->meta->make_immutable( inline_constructor => 0 );
+
+1;
diff --git a/perllib/FixMyStreet/DB/Result/AlertSent.pm b/perllib/FixMyStreet/DB/Result/AlertSent.pm
new file mode 100644
index 000000000..85a9000d5
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/AlertSent.pm
@@ -0,0 +1,38 @@
+package FixMyStreet::DB::Result::AlertSent;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime");
+__PACKAGE__->table("alert_sent");
+__PACKAGE__->add_columns(
+ "alert_id",
+ { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+ "parameter",
+ { data_type => "text", is_nullable => 1 },
+ "whenqueued",
+ {
+ data_type => "timestamp",
+ default_value => \"ms_current_timestamp()",
+ is_nullable => 0,
+ },
+);
+__PACKAGE__->belongs_to(
+ "alert",
+ "FixMyStreet::DB::Result::Alert",
+ { id => "alert_id" },
+ { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-06-03 16:48:36
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:x1nMIiNFSTKxdPxZmko18Q
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
diff --git a/perllib/FixMyStreet/DB/Result/AlertType.pm b/perllib/FixMyStreet/DB/Result/AlertType.pm
new file mode 100644
index 000000000..7a3cd1e36
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/AlertType.pm
@@ -0,0 +1,55 @@
+package FixMyStreet::DB::Result::AlertType;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime");
+__PACKAGE__->table("alert_type");
+__PACKAGE__->add_columns(
+ "ref",
+ { data_type => "text", is_nullable => 0 },
+ "head_sql_query",
+ { data_type => "text", is_nullable => 0 },
+ "head_table",
+ { data_type => "text", is_nullable => 0 },
+ "head_title",
+ { data_type => "text", is_nullable => 0 },
+ "head_link",
+ { data_type => "text", is_nullable => 0 },
+ "head_description",
+ { data_type => "text", is_nullable => 0 },
+ "item_table",
+ { data_type => "text", is_nullable => 0 },
+ "item_where",
+ { data_type => "text", is_nullable => 0 },
+ "item_order",
+ { data_type => "text", is_nullable => 0 },
+ "item_title",
+ { data_type => "text", is_nullable => 0 },
+ "item_link",
+ { data_type => "text", is_nullable => 0 },
+ "item_description",
+ { data_type => "text", is_nullable => 0 },
+ "template",
+ { data_type => "text", is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("ref");
+__PACKAGE__->has_many(
+ "alerts",
+ "FixMyStreet::DB::Result::Alert",
+ { "foreign.alert_type" => "self.ref" },
+ { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-06-03 16:48:36
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:KNZ7eWU/VgF8xzsjCHKVjw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
diff --git a/perllib/FixMyStreet/DB/Result/Comment.pm b/perllib/FixMyStreet/DB/Result/Comment.pm
new file mode 100644
index 000000000..68175dead
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/Comment.pm
@@ -0,0 +1,150 @@
+package FixMyStreet::DB::Result::Comment;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime");
+__PACKAGE__->table("comment");
+__PACKAGE__->add_columns(
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "comment_id_seq",
+ },
+ "problem_id",
+ { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+ "name",
+ { data_type => "text", is_nullable => 1 },
+ "website",
+ { data_type => "text", is_nullable => 1 },
+ "created",
+ {
+ data_type => "timestamp",
+ default_value => \"ms_current_timestamp()",
+ is_nullable => 0,
+ },
+ "confirmed",
+ { data_type => "timestamp", is_nullable => 1 },
+ "text",
+ { data_type => "text", is_nullable => 0 },
+ "photo",
+ { data_type => "bytea", is_nullable => 1 },
+ "state",
+ { data_type => "text", is_nullable => 0 },
+ "cobrand",
+ { data_type => "text", default_value => "", is_nullable => 0 },
+ "lang",
+ { data_type => "text", default_value => "en-gb", is_nullable => 0 },
+ "cobrand_data",
+ { data_type => "text", default_value => "", is_nullable => 0 },
+ "mark_fixed",
+ { data_type => "boolean", is_nullable => 0 },
+ "mark_open",
+ { data_type => "boolean", default_value => \"false", is_nullable => 0 },
+ "user_id",
+ { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+ "anonymous",
+ { data_type => "boolean", is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("id");
+__PACKAGE__->belongs_to(
+ "user",
+ "FixMyStreet::DB::Result::User",
+ { id => "user_id" },
+ { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+__PACKAGE__->belongs_to(
+ "problem",
+ "FixMyStreet::DB::Result::Problem",
+ { id => "problem_id" },
+ { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-05-24 15:32:43
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:71bSUgPf3uW607g2EGl/Vw
+
+use DateTime::TimeZone;
+use Image::Size;
+use Moose;
+use namespace::clean -except => [ 'meta' ];
+
+with 'FixMyStreet::Roles::Abuser';
+
+my $tz = DateTime::TimeZone->new( name => "local" );
+
+sub created_local {
+ my $self = shift;
+
+ return $self->created
+ ? $self->created->set_time_zone($tz)
+ : $self->created;
+}
+
+sub confirmed_local {
+ my $self = shift;
+
+ # if confirmed is null then it doesn't get inflated so don't
+ # try and set the timezone
+ return $self->confirmed
+ ? $self->confirmed->set_time_zone($tz)
+ : $self->confirmed;
+}
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+sub check_for_errors {
+ my $self = shift;
+
+ my %errors = ();
+
+ $errors{update} = _('Please enter a message')
+ unless $self->text =~ m/\S/;
+
+ return \%errors;
+}
+
+=head2 confirm
+
+Set state of comment to confirmed
+
+=cut
+
+sub confirm {
+ my $self = shift;
+
+ $self->state( 'confirmed' );
+ $self->confirmed( \'ms_current_timestamp()' );
+}
+
+=head2 get_photo_params
+
+Returns a hashref of details of any attached photo for use in templates.
+Hashref contains height, width and url keys.
+
+=cut
+
+sub get_photo_params {
+ my $self = shift;
+
+ return {} unless $self->photo;
+
+ my $photo = {};
+ ( $photo->{width}, $photo->{height} ) =
+ Image::Size::imgsize( \$self->photo );
+ $photo->{url} = '/photo?c=' . $self->id;
+
+ return $photo;
+}
+
+# we need the inline_constructor bit as we don't inherit from Moose
+__PACKAGE__->meta->make_immutable( inline_constructor => 0 );
+
+1;
diff --git a/perllib/FixMyStreet/DB/Result/Contact.pm b/perllib/FixMyStreet/DB/Result/Contact.pm
new file mode 100644
index 000000000..2b18e0250
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/Contact.pm
@@ -0,0 +1,45 @@
+package FixMyStreet::DB::Result::Contact;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime");
+__PACKAGE__->table("contacts");
+__PACKAGE__->add_columns(
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "contacts_id_seq",
+ },
+ "area_id",
+ { data_type => "integer", is_nullable => 0 },
+ "category",
+ { data_type => "text", default_value => "Other", is_nullable => 0 },
+ "email",
+ { data_type => "text", is_nullable => 0 },
+ "confirmed",
+ { data_type => "boolean", is_nullable => 0 },
+ "deleted",
+ { data_type => "boolean", is_nullable => 0 },
+ "editor",
+ { data_type => "text", is_nullable => 0 },
+ "whenedited",
+ { data_type => "timestamp", is_nullable => 0 },
+ "note",
+ { data_type => "text", is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("id");
+__PACKAGE__->add_unique_constraint("contacts_area_id_category_idx", ["area_id", "category"]);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-05-24 15:32:43
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:8u8S4QtIf0n7QUxVuP/Siw
+
+1;
diff --git a/perllib/FixMyStreet/DB/Result/ContactsHistory.pm b/perllib/FixMyStreet/DB/Result/ContactsHistory.pm
new file mode 100644
index 000000000..4fa74a9a2
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/ContactsHistory.pm
@@ -0,0 +1,48 @@
+package FixMyStreet::DB::Result::ContactsHistory;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime");
+__PACKAGE__->table("contacts_history");
+__PACKAGE__->add_columns(
+ "contacts_history_id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "contacts_history_contacts_history_id_seq",
+ },
+ "contact_id",
+ { data_type => "integer", is_nullable => 0 },
+ "area_id",
+ { data_type => "integer", is_nullable => 0 },
+ "category",
+ { data_type => "text", default_value => "Other", is_nullable => 0 },
+ "email",
+ { data_type => "text", is_nullable => 0 },
+ "confirmed",
+ { data_type => "boolean", is_nullable => 0 },
+ "deleted",
+ { data_type => "boolean", is_nullable => 0 },
+ "editor",
+ { data_type => "text", is_nullable => 0 },
+ "whenedited",
+ { data_type => "timestamp", is_nullable => 0 },
+ "note",
+ { data_type => "text", is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("contacts_history_id");
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-06-02 18:27:49
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:D9Uu5Lp8BackyZdLXJDIvw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
diff --git a/perllib/FixMyStreet/DB/Result/Nearby.pm b/perllib/FixMyStreet/DB/Result/Nearby.pm
new file mode 100644
index 000000000..d3d228788
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/Nearby.pm
@@ -0,0 +1,33 @@
+package FixMyStreet::DB::Result::Nearby;
+
+# Thanks to http://www.perlmonks.org/?node_id=633800
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+use Moose;
+use namespace::clean -except => [ 'meta' ];
+
+__PACKAGE__->table( 'NONE' );
+__PACKAGE__->add_columns(
+ "problem_id",
+ { data_type => "integer", is_nullable => 0 },
+ "distance",
+ { data_type => "double precision", is_nullable => 0 },
+);
+__PACKAGE__->belongs_to(
+ "problem",
+ "FixMyStreet::DB::Result::Problem",
+ { id => "problem_id" },
+ { is_deferrable => 1 },
+);
+
+# Make a new ResultSource based on the User class
+__PACKAGE__->result_source_instance
+ ->name( \'problem_find_nearby(?,?,?)' );
+
+# we need the inline_constructor bit as we don't inherit from Moose
+__PACKAGE__->meta->make_immutable( inline_constructor => 0 );
+
+1;
diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm
new file mode 100644
index 000000000..ab05c3157
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/Problem.pm
@@ -0,0 +1,386 @@
+package FixMyStreet::DB::Result::Problem;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime");
+__PACKAGE__->table("problem");
+__PACKAGE__->add_columns(
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "problem_id_seq",
+ },
+ "postcode",
+ { data_type => "text", is_nullable => 0 },
+ "latitude",
+ { data_type => "double precision", is_nullable => 0 },
+ "longitude",
+ { data_type => "double precision", is_nullable => 0 },
+ "council",
+ { data_type => "text", is_nullable => 1 },
+ "areas",
+ { data_type => "text", is_nullable => 0 },
+ "category",
+ { data_type => "text", default_value => "Other", is_nullable => 0 },
+ "title",
+ { data_type => "text", is_nullable => 0 },
+ "detail",
+ { data_type => "text", is_nullable => 0 },
+ "photo",
+ { data_type => "bytea", is_nullable => 1 },
+ "used_map",
+ { data_type => "boolean", is_nullable => 0 },
+ "user_id",
+ { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+ "name",
+ { data_type => "text", is_nullable => 0 },
+ "anonymous",
+ { data_type => "boolean", is_nullable => 0 },
+ "external_id",
+ { data_type => "text", is_nullable => 1 },
+ "external_body",
+ { data_type => "text", is_nullable => 1 },
+ "external_team",
+ { data_type => "text", is_nullable => 1 },
+ "created",
+ {
+ data_type => "timestamp",
+ default_value => \"ms_current_timestamp()",
+ is_nullable => 0,
+ },
+ "confirmed",
+ { data_type => "timestamp", is_nullable => 1 },
+ "state",
+ { data_type => "text", is_nullable => 0 },
+ "lang",
+ { data_type => "text", default_value => "en-gb", is_nullable => 0 },
+ "service",
+ { data_type => "text", default_value => "", is_nullable => 0 },
+ "cobrand",
+ { data_type => "text", default_value => "", is_nullable => 0 },
+ "cobrand_data",
+ { data_type => "text", default_value => "", is_nullable => 0 },
+ "lastupdate",
+ {
+ data_type => "timestamp",
+ default_value => \"ms_current_timestamp()",
+ is_nullable => 0,
+ },
+ "whensent",
+ { data_type => "timestamp", is_nullable => 1 },
+ "send_questionnaire",
+ { data_type => "boolean", default_value => \"true", is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("id");
+__PACKAGE__->has_many(
+ "comments",
+ "FixMyStreet::DB::Result::Comment",
+ { "foreign.problem_id" => "self.id" },
+ { cascade_copy => 0, cascade_delete => 0 },
+);
+__PACKAGE__->belongs_to(
+ "user",
+ "FixMyStreet::DB::Result::User",
+ { id => "user_id" },
+ { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+__PACKAGE__->has_many(
+ "questionnaires",
+ "FixMyStreet::DB::Result::Questionnaire",
+ { "foreign.problem_id" => "self.id" },
+ { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-05-24 15:32:43
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:U3aYCRwE4etekKaHdhEkIw
+
+# Add fake relationship to stored procedure table
+__PACKAGE__->has_many(
+ "nearby",
+ "FixMyStreet::DB::Result::Nearby",
+ { "foreign.problem_id" => "self.id" },
+ { cascade_copy => 0, cascade_delete => 0 },
+);
+
+use DateTime::TimeZone;
+use Image::Size;
+use Moose;
+use namespace::clean -except => [ 'meta' ];
+use Utils;
+
+with 'FixMyStreet::Roles::Abuser';
+
+my $tz = DateTime::TimeZone->new( name => "local" );
+
+sub confirmed_local {
+ my $self = shift;
+
+ return $self->confirmed
+ ? $self->confirmed->set_time_zone($tz)
+ : $self->confirmed;
+}
+
+sub created_local {
+ my $self = shift;
+
+ return $self->created
+ ? $self->created->set_time_zone($tz)
+ : $self->created;
+}
+
+sub whensent_local {
+ my $self = shift;
+
+ return $self->whensent
+ ? $self->whensent->set_time_zone($tz)
+ : $self->whensent;
+}
+
+sub lastupdate_local {
+ my $self = shift;
+
+ return $self->lastupdate
+ ? $self->lastupdate->set_time_zone($tz)
+ : $self->lastupdate;
+}
+
+around service => sub {
+ my ( $orig, $self ) = ( shift, shift );
+ my $s = $self->$orig(@_);
+ $s =~ s/_/ /g;
+ return $s;
+};
+
+=head2 check_for_errors
+
+ $error_hashref = $problem->check_for_errors();
+
+Look at all the fields and return a hashref with all errors found, keyed on the
+field name. This is intended to be passed back to the form to display the
+errors.
+
+TODO - ideally we'd pass back error codes which would be humanised in the
+templates (eg: 'missing','email_not_valid', etc).
+
+=cut
+
+sub check_for_errors {
+ my $self = shift;
+
+ my %errors = ();
+
+ $errors{title} = _('Please enter a subject')
+ unless $self->title =~ m/\S/;
+
+ $errors{detail} = _('Please enter some details')
+ unless $self->detail =~ m/\S/;
+
+ $errors{council} = _('No council selected')
+ unless $self->council
+ && $self->council =~ m/^(?:-1|[\d,]+(?:\|[\d,]+)?)$/;
+
+ if ( $self->name !~ m/\S/ ) {
+ $errors{name} = _('Please enter your name');
+ }
+ elsif (length( $self->name ) < 5
+ || $self->name !~ m/\s/
+ || $self->name =~ m/\ba\s*n+on+((y|o)mo?u?s)?(ly)?\b/i )
+ {
+ $errors{name} = _(
+'Please enter your full name, councils need this information - if you do not wish your name to be shown on the site, untick the box'
+ );
+ }
+
+ if ( $self->category
+ && $self->category eq _('-- Pick a category --') )
+ {
+ $errors{category} = _('Please choose a category');
+ $self->category(undef);
+ }
+ elsif ($self->category
+ && $self->category eq _('-- Pick a property type --') )
+ {
+ $errors{category} = _('Please choose a property type');
+ $self->category(undef);
+ }
+
+ return \%errors;
+}
+
+=head2 confirm
+
+ $bool = $problem->confirm( );
+ $problem->update;
+
+
+Set the state to 'confirmed' and put current time into 'confirmed' field. This
+is a no-op if the report is already confirmed.
+
+NOTE - does not update storage - call update or insert to do that.
+
+=cut
+
+sub confirm {
+ my $self = shift;
+
+ return if $self->state eq 'confirmed';
+
+ $self->state('confirmed');
+ $self->confirmed( \'ms_current_timestamp()' );
+ return 1;
+}
+
+=head2 councils
+
+Returns an array of councils to which a report was sent.
+
+=cut
+
+sub councils {
+ my $self = shift;
+ return () unless $self->council;
+ (my $council = $self->council) =~ s/\|.*$//;
+ my @council = split( /,/, $council );
+ return @council;
+}
+
+=head2 url
+
+Returns a URL for this problem report.
+
+=cut
+
+sub url {
+ my $self = shift;
+ return "/report/" . $self->id;
+}
+
+=head2 get_photo_params
+
+Returns a hashref of details of any attached photo for use in templates.
+Hashref contains height, width and url keys.
+
+=cut
+
+sub get_photo_params {
+ my $self = shift;
+
+ return {} unless $self->photo;
+
+ my $photo = {};
+ ( $photo->{width}, $photo->{height} ) =
+ Image::Size::imgsize( \$self->photo );
+ $photo->{url} = '/photo?id=' . $self->id;
+
+ return $photo;
+}
+
+=head2 meta_line
+
+Returns a string to be used on a problem report page, describing some of the
+meta data about the report.
+
+=cut
+
+sub meta_line {
+ my ( $problem, $c ) = @_;
+
+ my $date_time =
+ Utils::prettify_epoch( $problem->confirmed_local->epoch );
+ my $meta = '';
+
+ # FIXME Should be in cobrand
+ if ($c->cobrand->moniker eq 'emptyhomes') {
+
+ my $category = _($problem->category);
+ utf8::decode($category);
+ if ($problem->anonymous) {
+ $meta = sprintf(_('%s, reported anonymously at %s'), $category, $date_time);
+ } else {
+ $meta = sprintf(_('%s, reported by %s at %s'), $category, $problem->name, $date_time);
+ }
+
+ } else {
+
+ if ( $problem->anonymous ) {
+ if ( $problem->service
+ and $problem->category && $problem->category ne _('Other') )
+ {
+ $meta =
+ sprintf( _('Reported by %s in the %s category anonymously at %s'),
+ $problem->service, $problem->category, $date_time );
+ }
+ elsif ( $problem->service ) {
+ $meta = sprintf( _('Reported by %s anonymously at %s'),
+ $problem->service, $date_time );
+ }
+ elsif ( $problem->category and $problem->category ne _('Other') ) {
+ $meta = sprintf( _('Reported in the %s category anonymously at %s'),
+ $problem->category, $date_time );
+ }
+ else {
+ $meta = sprintf( _('Reported anonymously at %s'), $date_time );
+ }
+ }
+ else {
+ if ( $problem->service
+ and $problem->category && $problem->category ne _('Other') )
+ {
+ $meta = sprintf(
+ _('Reported by %s in the %s category by %s at %s'),
+ $problem->service, $problem->category,
+ $problem->name, $date_time
+ );
+ }
+ elsif ( $problem->service ) {
+ $meta = sprintf( _('Reported by %s by %s at %s'),
+ $problem->service, $problem->name, $date_time );
+ }
+ elsif ( $problem->category and $problem->category ne _('Other') ) {
+ $meta = sprintf( _('Reported in the %s category by %s at %s'),
+ $problem->category, $problem->name, $date_time );
+ }
+ else {
+ $meta =
+ sprintf( _('Reported by %s at %s'), $problem->name, $date_time );
+ }
+ }
+
+ }
+
+ $meta .= $c->cobrand->extra_problem_meta_text($problem);
+ $meta .= '; ' . _('the map was not used so pin location may be inaccurate')
+ unless $problem->used_map;
+
+ return $meta;
+}
+
+sub duration_string {
+ my $problem = shift;
+ my $body;
+ if ($problem->external_body) {
+ $body = $problem->external_body;
+ } else {
+ (my $council = $problem->council) =~ s/\|.*//g;
+ my @councils = split( /,/, $council );
+ my $areas_info = mySociety::MaPit::call('areas', \@councils);
+ $body = join(' and ', map { $areas_info->{$_}->{name} } @councils);
+ }
+ return sprintf(_('Sent to %s %s later'), $body,
+ Utils::prettify_duration($problem->whensent_local->epoch - $problem->confirmed_local->epoch, 'minute')
+ );
+}
+
+# we need the inline_constructor bit as we don't inherit from Moose
+__PACKAGE__->meta->make_immutable( inline_constructor => 0 );
+
+1;
diff --git a/perllib/FixMyStreet/DB/Result/Questionnaire.pm b/perllib/FixMyStreet/DB/Result/Questionnaire.pm
new file mode 100644
index 000000000..5a507645f
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/Questionnaire.pm
@@ -0,0 +1,66 @@
+package FixMyStreet::DB::Result::Questionnaire;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime");
+__PACKAGE__->table("questionnaire");
+__PACKAGE__->add_columns(
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "questionnaire_id_seq",
+ },
+ "problem_id",
+ { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+ "whensent",
+ { data_type => "timestamp", is_nullable => 0 },
+ "whenanswered",
+ { data_type => "timestamp", is_nullable => 1 },
+ "ever_reported",
+ { data_type => "boolean", is_nullable => 1 },
+ "old_state",
+ { data_type => "text", is_nullable => 1 },
+ "new_state",
+ { data_type => "text", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+__PACKAGE__->belongs_to(
+ "problem",
+ "FixMyStreet::DB::Result::Problem",
+ { id => "problem_id" },
+ { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-05-24 15:32:43
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:BAWTYKAQ84VeOI6D2gtQOQ
+
+use DateTime::TimeZone;
+
+my $tz = DateTime::TimeZone->new( name => "local" );
+
+sub whensent_local {
+ my $self = shift;
+
+ return $self->whensent
+ ? $self->whensent->set_time_zone($tz)
+ : $self->whensent;
+}
+
+sub whenanswered_local {
+ my $self = shift;
+
+ return $self->whenanswered
+ ? $self->whenanswered->set_time_zone($tz)
+ : $self->whenanswered;
+}
+
+1;
diff --git a/perllib/FixMyStreet/DB/Result/Secret.pm b/perllib/FixMyStreet/DB/Result/Secret.pm
new file mode 100644
index 000000000..399f0be18
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/Secret.pm
@@ -0,0 +1,21 @@
+package FixMyStreet::DB::Result::Secret;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime");
+__PACKAGE__->table("secret");
+__PACKAGE__->add_columns("secret", { data_type => "text", is_nullable => 0 });
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-06-03 12:02:18
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Htl6+DHfHy9l+bjBxAbH6Q
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
diff --git a/perllib/FixMyStreet/DB/Result/Session.pm b/perllib/FixMyStreet/DB/Result/Session.pm
new file mode 100644
index 000000000..a10e6fa0d
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/Session.pm
@@ -0,0 +1,28 @@
+package FixMyStreet::DB::Result::Session;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime");
+__PACKAGE__->table("sessions");
+__PACKAGE__->add_columns(
+ "id",
+ { data_type => "char", is_nullable => 0, size => 72 },
+ "session_data",
+ { data_type => "text", is_nullable => 1 },
+ "expires",
+ { data_type => "integer", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-05-24 15:32:43
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:tYWQtbja2nkA/2A+kaPl1g
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
diff --git a/perllib/FixMyStreet/DB/Result/Token.pm b/perllib/FixMyStreet/DB/Result/Token.pm
new file mode 100644
index 000000000..d838e9fc2
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/Token.pm
@@ -0,0 +1,87 @@
+package FixMyStreet::DB::Result::Token;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime");
+__PACKAGE__->table("token");
+__PACKAGE__->add_columns(
+ "scope",
+ { data_type => "text", is_nullable => 0 },
+ "token",
+ { data_type => "text", is_nullable => 0 },
+ "data",
+ { data_type => "bytea", is_nullable => 0 },
+ "created",
+ {
+ data_type => "timestamp",
+ default_value => \"ms_current_timestamp()",
+ is_nullable => 0,
+ },
+);
+__PACKAGE__->set_primary_key("scope", "token");
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-05-24 15:32:43
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ET4L1y6yWmEUDKPKa7wJiw
+
+# Trying not to use this
+# use mySociety::DBHandle qw(dbh);
+
+use mySociety::AuthToken;
+use IO::String;
+use RABX;
+
+=head1 NAME
+
+FixMyStreet::DB::Result::Token
+
+=head2 DESCRIPTION
+
+Representation of mySociety::AuthToken in the DBIx::Class world.
+
+Mostly done so that we don't need to use mySociety::DBHandle.
+
+The 'data' value is automatically inflated and deflated in the same way that the
+AuthToken would do it. 'token' is set to a new random value by default and the
+'created' timestamp is achieved using the database function
+ms_current_timestamp.
+
+=cut
+
+__PACKAGE__->filter_column(
+ data => {
+ filter_from_storage => sub {
+ my $self = shift;
+ my $ser = shift;
+ return undef unless defined $ser;
+ my $h = new IO::String($ser);
+ return RABX::wire_rd($h);
+ },
+ filter_to_storage => sub {
+ my $self = shift;
+ my $data = shift;
+ my $ser = '';
+ my $h = new IO::String($ser);
+ RABX::wire_wr( $data, $h );
+ return $ser;
+ },
+ }
+);
+
+sub new {
+ my ( $class, $attrs ) = @_;
+
+ $attrs->{token} ||= mySociety::AuthToken::random_token();
+ $attrs->{created} ||= \'ms_current_timestamp()';
+
+ my $new = $class->next::method($attrs);
+ return $new;
+}
+
+1;
diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm
new file mode 100644
index 000000000..30314ffa3
--- /dev/null
+++ b/perllib/FixMyStreet/DB/Result/User.pm
@@ -0,0 +1,118 @@
+package FixMyStreet::DB::Result::User;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("FilterColumn", "InflateColumn::DateTime");
+__PACKAGE__->table("users");
+__PACKAGE__->add_columns(
+ "id",
+ {
+ data_type => "integer",
+ is_auto_increment => 1,
+ is_nullable => 0,
+ sequence => "users_id_seq",
+ },
+ "email",
+ { data_type => "text", is_nullable => 0 },
+ "name",
+ { data_type => "text", is_nullable => 1 },
+ "phone",
+ { data_type => "text", is_nullable => 1 },
+ "password",
+ { data_type => "text", default_value => "", is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("id");
+__PACKAGE__->add_unique_constraint("users_email_key", ["email"]);
+__PACKAGE__->has_many(
+ "alerts",
+ "FixMyStreet::DB::Result::Alert",
+ { "foreign.user_id" => "self.id" },
+ { cascade_copy => 0, cascade_delete => 0 },
+);
+__PACKAGE__->has_many(
+ "comments",
+ "FixMyStreet::DB::Result::Comment",
+ { "foreign.user_id" => "self.id" },
+ { cascade_copy => 0, cascade_delete => 0 },
+);
+__PACKAGE__->has_many(
+ "problems",
+ "FixMyStreet::DB::Result::Problem",
+ { "foreign.user_id" => "self.id" },
+ { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-05-24 15:32:43
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Vo92xIsCQTLF6lBugzhHcA
+
+use mySociety::EmailUtil;
+
+=head2 check_for_errors
+
+ $error_hashref = $problem->check_for_errors();
+
+Look at all the fields and return a hashref with all errors found, keyed on the
+field name. This is intended to be passed back to the form to display the
+errors.
+
+TODO - ideally we'd pass back error codes which would be humanised in the
+templates (eg: 'missing','email_not_valid', etc).
+
+=cut
+
+sub check_for_errors {
+ my $self = shift;
+
+ my %errors = ();
+
+ if ( $self->name !~ m/\S/ ) {
+ $errors{name} = _('Please enter your name');
+ }
+ elsif (length( $self->name ) < 5
+ || $self->name !~ m/\s/
+ || $self->name =~ m/\ba\s*n+on+((y|o)mo?u?s)?(ly)?\b/i )
+ {
+ $errors{name} = _(
+'Please enter your full name, councils need this information - if you do not wish your name to be shown on the site, untick the box'
+ );
+ }
+
+ if ( $self->email !~ /\S/ ) {
+ $errors{email} = _('Please enter your email');
+ }
+ elsif ( !mySociety::EmailUtil::is_valid_email( $self->email ) ) {
+ $errors{email} = _('Please enter a valid email');
+ }
+
+ return \%errors;
+}
+
+=head2 answered_ever_reported
+
+Check if the user has ever answered a questionnaire.
+
+=cut
+
+sub answered_ever_reported {
+ my $self = shift;
+
+ my $has_answered =
+ $self->result_source->schema->resultset('Questionnaire')->search(
+ {
+ ever_reported => { not => undef },
+ problem_id => { -in =>
+ $self->problems->get_column('id')->as_query },
+ }
+ );
+
+ return $has_answered->count > 0;
+}
+
+1;
diff --git a/perllib/FixMyStreet/DB/ResultSet/Alert.pm b/perllib/FixMyStreet/DB/ResultSet/Alert.pm
new file mode 100644
index 000000000..5848265f1
--- /dev/null
+++ b/perllib/FixMyStreet/DB/ResultSet/Alert.pm
@@ -0,0 +1,50 @@
+package FixMyStreet::DB::ResultSet::Alert;
+use base 'DBIx::Class::ResultSet';
+
+use strict;
+use warnings;
+
+sub timeline_created {
+ my ( $rs, $restriction ) = @_;
+
+ my $prefetch =
+ FixMyStreet::App->model('DB')->schema->storage->sql_maker->quote_char ?
+ [ qw/alert_type user/ ] :
+ [ qw/alert_type/ ];
+
+ return $rs->search(
+ {
+ whensubscribed => { '>=', \"ms_current_timestamp()-'7 days'::interval" },
+ confirmed => 1,
+ %{ $restriction },
+ },
+ {
+ prefetch => $prefetch,
+ }
+ );
+}
+
+sub timeline_disabled {
+ my ( $rs, $restriction ) = @_;
+
+ return $rs->search(
+ {
+ whendisabled => { '>=', \"ms_current_timestamp()-'7 days'::interval" },
+ %{ $restriction },
+ },
+ );
+}
+
+sub summary_count {
+ my ( $rs, $restriction ) = @_;
+
+ return $rs->search(
+ $restriction,
+ {
+ group_by => ['confirmed'],
+ select => [ 'confirmed', { count => 'id' } ],
+ as => [qw/confirmed confirmed_count/]
+ }
+ );
+}
+1;
diff --git a/perllib/FixMyStreet/DB/ResultSet/AlertType.pm b/perllib/FixMyStreet/DB/ResultSet/AlertType.pm
new file mode 100644
index 000000000..405210861
--- /dev/null
+++ b/perllib/FixMyStreet/DB/ResultSet/AlertType.pm
@@ -0,0 +1,204 @@
+package FixMyStreet::DB::ResultSet::AlertType;
+use base 'DBIx::Class::ResultSet';
+
+use strict;
+use warnings;
+
+use File::Slurp;
+
+use mySociety::DBHandle qw(dbh);
+use mySociety::EmailUtil;
+use mySociety::Gaze;
+use mySociety::Locale;
+use mySociety::MaPit;
+
+# Child must have confirmed, id, email, state(!) columns
+# If parent/child, child table must also have name and text
+# and foreign key to parent must be PARENT_id
+sub email_alerts ($) {
+ my ( $rs ) = @_;
+
+ my $q = $rs->search( { ref => { -not_like => '%local_problems%' } } );
+ while (my $alert_type = $q->next) {
+ my $ref = $alert_type->ref;
+ my $head_table = $alert_type->head_table;
+ my $item_table = $alert_type->item_table;
+ my $query = 'select alert.id as alert_id, alert_user.email as alert_email, alert.lang as alert_lang, alert.cobrand as alert_cobrand,
+ alert.cobrand_data as alert_cobrand_data, alert.parameter as alert_parameter, alert.parameter2 as alert_parameter2, ';
+ if ($head_table) {
+ $query .= "
+ $item_table.id as item_id, $item_table.name as item_name, $item_table.text as item_text,
+ $head_table.*
+ from alert
+ inner join $item_table on alert.parameter::integer = $item_table.${head_table}_id
+ inner join $head_table on alert.parameter::integer = $head_table.id
+ inner join users as alert_user on alert.user_id = alert_user.id";
+ } else {
+ $query .= " $item_table.*,
+ $item_table.id as item_id
+ from alert
+ cross join $item_table
+ inner join users as alert_user on alert.user_id = alert_user.id";
+ }
+ $query .= "
+ where alert_type='$ref' and whendisabled is null and $item_table.confirmed >= whensubscribed
+ and $item_table.confirmed >= ms_current_timestamp() - '7 days'::interval
+ and (select whenqueued from alert_sent where alert_sent.alert_id = alert.id and alert_sent.parameter::integer = $item_table.id) is null
+ and $item_table.user_id <> alert.user_id
+ and " . $alert_type->item_where . "
+ and alert.confirmed = 1
+ order by alert.id, $item_table.confirmed";
+ # XXX Ugh - needs work
+ $query =~ s/\?/alert.parameter/ if ($query =~ /\?/);
+ $query =~ s/\?/alert.parameter2/ if ($query =~ /\?/);
+ $query = dbh()->prepare($query);
+ $query->execute();
+ my $last_alert_id;
+ my %data = ( template => $alert_type->template, data => '' );
+ while (my $row = $query->fetchrow_hashref) {
+
+ my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($row->{alert_cobrand})->new();
+
+ # Cobranded and non-cobranded messages can share a database. In this case, the conf file
+ # should specify a vhost to send the reports for each cobrand, so that they don't get sent
+ # more than once if there are multiple vhosts running off the same database. The email_host
+ # call checks if this is the host that sends mail for this cobrand.
+ next unless $cobrand->email_host;
+
+ FixMyStreet::App->model('DB::AlertSent')->create( {
+ alert_id => $row->{alert_id},
+ parameter => $row->{item_id},
+ } );
+ if ($last_alert_id && $last_alert_id != $row->{alert_id}) {
+ _send_aggregated_alert_email(%data);
+ %data = ( template => $alert_type->template, data => '' );
+ }
+
+ # create problem status message for the templates
+ $data{state_message} =
+ $row->{state} eq 'fixed'
+ ? _("This report is currently marked as fixed.")
+ : _("This report is currently marked as open.");
+
+ my $url = $cobrand->base_url_for_emails( $row->{alert_cobrand_data} );
+ if ($row->{item_text}) {
+ $data{problem_url} = $url . "/report/" . $row->{id};
+ $data{data} .= $row->{item_name} . ' : ' if $row->{item_name};
+ $data{data} .= $row->{item_text} . "\n\n------\n\n";
+ } else {
+ $data{data} .= $url . "/report/" . $row->{id} . " - $row->{title}\n\n";
+ }
+ if (!$data{alert_email}) {
+ %data = (%data, %$row);
+ if ($ref eq 'area_problems' || $ref eq 'council_problems' || $ref eq 'ward_problems') {
+ my $va_info = mySociety::MaPit::call('area', $row->{alert_parameter});
+ $data{area_name} = $va_info->{name};
+ }
+ if ($ref eq 'ward_problems') {
+ my $va_info = mySociety::MaPit::call('area', $row->{alert_parameter2});
+ $data{ward_name} = $va_info->{name};
+ }
+ }
+ $data{cobrand} = $row->{alert_cobrand};
+ $data{cobrand_data} = $row->{alert_cobrand_data};
+ $data{lang} = $row->{alert_lang};
+ $last_alert_id = $row->{alert_id};
+ }
+ if ($last_alert_id) {
+ _send_aggregated_alert_email(%data);
+ }
+ }
+
+ # Nearby done separately as the table contains the parameters
+ my $template = $rs->find( { ref => 'local_problems' } )->template;
+ my $query = FixMyStreet::App->model('DB::Alert')->search( {
+ alert_type => 'local_problems',
+ whendisabled => undef,
+ confirmed => 1
+ }, {
+ order_by => 'id'
+ } );
+ while (my $alert = $query->next) {
+ my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($alert->cobrand)->new();
+ next unless $cobrand->email_host;
+
+ my $longitude = $alert->parameter;
+ my $latitude = $alert->parameter2;
+ my $url = $cobrand->base_url_for_emails( $alert->cobrand_data );
+ my ($site_restriction, $site_id) = $cobrand->site_restriction( $alert->cobrand_data );
+ my $d = mySociety::Gaze::get_radius_containing_population($latitude, $longitude, 200000);
+ # Convert integer to GB locale string (with a ".")
+ $d = mySociety::Locale::in_gb_locale {
+ sprintf("%f", int($d*10+0.5)/10);
+ };
+ my %data = ( template => $template, data => '', alert_id => $alert->id, alert_email => $alert->user->email, lang => $alert->lang, cobrand => $alert->cobrand, cobrand_data => $alert->cobrand_data );
+ my $q = "select problem.id, problem.title from problem_find_nearby(?, ?, ?) as nearby, problem, users
+ where nearby.problem_id = problem.id
+ and problem.user_id = users.id
+ and problem.state in ('confirmed', 'fixed')
+ and problem.confirmed >= ? and problem.confirmed >= ms_current_timestamp() - '7 days'::interval
+ and (select whenqueued from alert_sent where alert_sent.alert_id = ? and alert_sent.parameter::integer = problem.id) is null
+ and users.email <> ?
+ $site_restriction
+ order by confirmed desc";
+ $q = dbh()->prepare($q);
+ $q->execute($latitude, $longitude, $d, $alert->whensubscribed, $alert->id, $alert->user->email);
+ while (my $row = $q->fetchrow_hashref) {
+ FixMyStreet::App->model('DB::AlertSent')->create( {
+ alert_id => $alert->id,
+ parameter => $row->{id},
+ } );
+ $data{data} .= $url . "/report/" . $row->{id} . " - $row->{title}\n\n";
+ }
+ _send_aggregated_alert_email(%data) if $data{data};
+ }
+}
+
+sub _send_aggregated_alert_email(%) {
+ my %data = @_;
+
+ my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($data{cobrand})->new();
+
+ $cobrand->set_lang_and_domain( $data{lang}, 1 );
+
+ my $token = FixMyStreet::App->model("DB::Token")->new_result( {
+ scope => 'alert',
+ data => {
+ id => $data{alert_id},
+ type => 'unsubscribe',
+ email => $data{alert_email},
+ }
+ } );
+ $data{unsubscribe_url} = $cobrand->base_url_for_emails( $data{cobrand_data} ) . '/A/' . $token->token;
+
+ my $template = FixMyStreet->path_to(
+ "templates", "email", $cobrand->moniker, "$data{template}.txt"
+ )->stringify;
+ my $template_cobrand = FixMyStreet->path_to(
+ "templates", "email", $cobrand->moniker, $data{lang}, "$data{template}.txt"
+ )->stringify;
+ $template = $template_cobrand if -e $template_cobrand;
+ $template = File::Slurp::read_file($template);
+
+ my $sender = $cobrand->contact_email;
+ (my $from = $sender) =~ s/team/fms-DO-NOT-REPLY/; # XXX
+ my $result = FixMyStreet::App->send_email_cron(
+ {
+ _template_ => $template,
+ _parameters_ => \%data,
+ From => [ $from, _($cobrand->contact_name) ],
+ To => $data{alert_email},
+ },
+ $sender,
+ [ $data{alert_email} ],
+ 0,
+ );
+
+ if ($result == mySociety::EmailUtil::EMAIL_SUCCESS) {
+ $token->insert();
+ } else {
+ print "Failed to send alert $data{alert_id}!";
+ }
+}
+
+1;
diff --git a/perllib/FixMyStreet/DB/ResultSet/Comment.pm b/perllib/FixMyStreet/DB/ResultSet/Comment.pm
new file mode 100644
index 000000000..70f8027aa
--- /dev/null
+++ b/perllib/FixMyStreet/DB/ResultSet/Comment.pm
@@ -0,0 +1,41 @@
+package FixMyStreet::DB::ResultSet::Comment;
+use base 'DBIx::Class::ResultSet';
+
+use strict;
+use warnings;
+
+sub timeline {
+ my ( $rs, $restriction ) = @_;
+
+ my $prefetch =
+ FixMyStreet::App->model('DB')->schema->storage->sql_maker->quote_char ?
+ [ qw/user/ ] :
+ [];
+
+ return $rs->search(
+ {
+ state => 'confirmed',
+ created => { '>=', \"ms_current_timestamp()-'7 days'::interval" },
+ %{ $restriction },
+ },
+ {
+ prefetch => $prefetch,
+ }
+ );
+}
+
+sub summary_count {
+ my ( $rs, $restriction ) = @_;
+
+ return $rs->search(
+ $restriction,
+ {
+ group_by => ['me.state'],
+ select => [ 'me.state', { count => 'me.id' } ],
+ as => [qw/state state_count/],
+ join => 'problem'
+ }
+ );
+}
+
+1;
diff --git a/perllib/FixMyStreet/DB/ResultSet/Contact.pm b/perllib/FixMyStreet/DB/ResultSet/Contact.pm
new file mode 100644
index 000000000..6fa6a03a0
--- /dev/null
+++ b/perllib/FixMyStreet/DB/ResultSet/Contact.pm
@@ -0,0 +1,33 @@
+package FixMyStreet::DB::ResultSet::Contact;
+use base 'DBIx::Class::ResultSet';
+
+use strict;
+use warnings;
+
+=head2 not_deleted
+
+ $rs = $rs->not_deleted();
+
+Filter down to not deleted contacts - which have C<deleted> set to false;
+
+=cut
+
+sub not_deleted {
+ my $rs = shift;
+ return $rs->search( { deleted => 0 } );
+}
+
+sub summary_count {
+ my ( $rs, $restriction ) = @_;
+
+ return $rs->search(
+ $restriction,
+ {
+ group_by => ['confirmed'],
+ select => [ 'confirmed', { count => 'id' } ],
+ as => [qw/confirmed confirmed_count/]
+ }
+ );
+}
+
+1;
diff --git a/perllib/FixMyStreet/DB/ResultSet/Nearby.pm b/perllib/FixMyStreet/DB/ResultSet/Nearby.pm
new file mode 100644
index 000000000..3b3a3d90b
--- /dev/null
+++ b/perllib/FixMyStreet/DB/ResultSet/Nearby.pm
@@ -0,0 +1,50 @@
+package FixMyStreet::DB::ResultSet::Nearby;
+use base 'DBIx::Class::ResultSet';
+
+use strict;
+use warnings;
+
+sub nearby {
+ my ( $rs, $c, $dist, $ids, $limit, $mid_lat, $mid_lon, $interval ) = @_;
+
+ my $params = {
+ state => [ 'confirmed', 'fixed' ],
+ };
+ $params->{'current_timestamp-lastupdate'} = { '<', \"'$interval'::interval" }
+ if $interval;
+ $params->{id} = { -not_in => $ids }
+ if $ids;
+ $params = {
+ %{ $c->cobrand->problems_clause },
+ %$params
+ } if $c->cobrand->problems_clause;
+
+ my $attrs = {
+ join => 'problem',
+ columns => [
+ 'problem.id', 'problem.title', 'problem.latitude',
+ 'problem.longitude', 'distance', 'problem.state',
+ 'problem.confirmed'
+ ],
+ bind => [ $mid_lat, $mid_lon, $dist ],
+ order_by => [ 'distance', { -desc => 'created' } ],
+ rows => $limit,
+ };
+
+ my @problems = mySociety::Locale::in_gb_locale { $rs->search( $params, $attrs )->all };
+ return \@problems;
+}
+
+# XXX Not currently used, so not migrating at present.
+#sub fixed_nearby {
+# my ($dist, $mid_lat, $mid_lon) = @_;
+# mySociety::Locale::in_gb_locale { select_all(
+# "select id, title, latitude, longitude, distance
+# from problem_find_nearby(?, ?, $dist) as nearby, problem
+# where nearby.problem_id = problem.id and state='fixed'
+# site_restriction
+# order by lastupdate desc", $mid_lat, $mid_lon);
+# }
+#}
+
+1;
diff --git a/perllib/FixMyStreet/DB/ResultSet/Problem.pm b/perllib/FixMyStreet/DB/ResultSet/Problem.pm
new file mode 100644
index 000000000..589745dff
--- /dev/null
+++ b/perllib/FixMyStreet/DB/ResultSet/Problem.pm
@@ -0,0 +1,175 @@
+package FixMyStreet::DB::ResultSet::Problem;
+use base 'DBIx::Class::ResultSet';
+
+use strict;
+use warnings;
+
+my $site_restriction;
+my $site_key;
+
+sub set_restriction {
+ my ( $rs, $sql, $key, $restriction ) = @_;
+ $site_key = $key;
+ $site_restriction = $restriction;
+}
+
+# Front page statistics
+
+sub recent_fixed {
+ my $rs = shift;
+ my $key = "recent_fixed:$site_key";
+ my $result = Memcached::get($key);
+ unless ($result) {
+ $result = $rs->search( {
+ state => 'fixed',
+ lastupdate => { '>', \"current_timestamp-'1 month'::interval" },
+ } )->count;
+ Memcached::set($key, $result, 3600);
+ }
+ return $result;
+}
+
+sub number_comments {
+ my $rs = shift;
+ my $key = "number_comments:$site_key";
+ my $result = Memcached::get($key);
+ unless ($result) {
+ $result = $rs->search(
+ { 'comments.state' => 'confirmed' },
+ { join => 'comments' }
+ )->count;
+ Memcached::set($key, $result, 3600);
+ }
+ return $result;
+}
+
+sub recent_new {
+ my ( $rs, $interval ) = @_;
+ (my $key = $interval) =~ s/\s+//g;
+ $key = "recent_new:$site_key:$key";
+ my $result = Memcached::get($key);
+ unless ($result) {
+ $result = $rs->search( {
+ state => [ 'confirmed', 'fixed' ],
+ confirmed => { '>', \"current_timestamp-'$interval'::interval" },
+ } )->count;
+ Memcached::set($key, $result, 3600);
+ }
+ return $result;
+}
+
+# Front page recent lists
+
+sub recent {
+ my ( $rs ) = @_;
+ my $key = "recent:$site_key";
+ my $result = Memcached::get($key);
+ unless ($result) {
+ $result = [ $rs->search( {
+ state => [ 'confirmed', 'fixed' ]
+ }, {
+ columns => [ 'id', 'title' ],
+ order_by => { -desc => 'confirmed' },
+ rows => 5,
+ } )->all ];
+ Memcached::set($key, $result, 3600);
+ }
+ return $result;
+}
+
+sub recent_photos {
+ my ( $rs, $num, $lat, $lon, $dist ) = @_;
+ my $probs;
+ my $query = {
+ state => [ 'confirmed', 'fixed' ],
+ photo => { '!=', undef },
+ };
+ my $attrs = {
+ columns => [ 'id', 'title' ],
+ order_by => { -desc => 'confirmed' },
+ rows => $num,
+ };
+ if (defined $lat) {
+ my $dist2 = $dist; # Create a copy of the variable to stop it being stringified into a locale in the next line!
+ my $key = "recent_photos:$site_key:$num:$lat:$lon:$dist2";
+ $probs = Memcached::get($key);
+ unless ($probs) {
+ $attrs->{bind} = [ $lat, $lon, $dist ];
+ $attrs->{join} = 'nearby';
+ $probs = [ mySociety::Locale::in_gb_locale {
+ $rs->search( $query, $attrs )->all;
+ } ];
+ Memcached::set($key, $probs, 3600);
+ }
+ } else {
+ my $key = "recent_photos:$site_key:$num";
+ $probs = Memcached::get($key);
+ unless ($probs) {
+ $probs = [ $rs->search( $query, $attrs )->all ];
+ Memcached::set($key, $probs, 3600);
+ }
+ }
+ return $probs;
+}
+
+# Problems around a location
+
+sub around_map {
+ my ( $rs, $min_lat, $max_lat, $min_lon, $max_lon, $interval, $limit ) = @_;
+ my $attr = {
+ order_by => { -desc => 'created' },
+ columns => [
+ 'id', 'title' ,'latitude', 'longitude', 'state', 'confirmed'
+ ],
+ };
+ $attr->{rows} = $limit if $limit;
+
+ my $q = {
+ state => [ 'confirmed', 'fixed' ],
+ latitude => { '>=', $min_lat, '<', $max_lat },
+ longitude => { '>=', $min_lon, '<', $max_lon },
+ };
+ $q->{'current_timestamp - lastupdate'} = { '<', \"'$interval'::interval" };
+
+ my @problems = mySociety::Locale::in_gb_locale { $rs->search( $q, $attr )->all };
+ return \@problems;
+}
+
+# Admin functions
+
+sub timeline {
+ my ( $rs ) = @_;
+
+ my $prefetch =
+ FixMyStreet::App->model('DB')->schema->storage->sql_maker->quote_char ?
+ [ qw/user/ ] :
+ [];
+
+ return $rs->search(
+ {
+ -or => {
+ created => { '>=', \"ms_current_timestamp()-'7 days'::interval" },
+ confirmed => { '>=', \"ms_current_timestamp()-'7 days'::interval" },
+ whensent => { '>=', \"ms_current_timestamp()-'7 days'::interval" },
+ }
+ },
+ {
+ prefetch => $prefetch,
+ }
+ );
+}
+
+sub summary_count {
+ my ( $rs, $restriction ) = @_;
+
+ return $rs->search(
+ $restriction,
+ {
+ group_by => ['state'],
+ select => [ 'state', { count => 'id' } ],
+ as => [qw/state state_count/]
+ }
+ );
+}
+
+1;
diff --git a/perllib/FixMyStreet/DB/ResultSet/Questionnaire.pm b/perllib/FixMyStreet/DB/ResultSet/Questionnaire.pm
new file mode 100644
index 000000000..ee15f8308
--- /dev/null
+++ b/perllib/FixMyStreet/DB/ResultSet/Questionnaire.pm
@@ -0,0 +1,146 @@
+package FixMyStreet::DB::ResultSet::Questionnaire;
+use base 'DBIx::Class::ResultSet';
+
+use strict;
+use warnings;
+use File::Slurp;
+use Utils;
+use mySociety::EmailUtil;
+
+sub send_questionnaires {
+ my ( $rs, $params ) = @_;
+ $rs->send_questionnaires_period( '4 weeks', $params );
+ $rs->send_questionnaires_period( '26 weeks', $params )
+ if $params->{site} eq 'emptyhomes';
+}
+
+sub send_questionnaires_period {
+ my ( $rs, $period, $params ) = @_;
+
+ # Select all problems that need a questionnaire email sending
+ my $q_params = {
+ state => [ 'confirmed', 'fixed' ],
+ whensent => [
+ '-and',
+ { '!=', undef },
+ { '<', \"ms_current_timestamp() - '$period'::interval" },
+ ],
+ send_questionnaire => 1,
+ };
+ # FIXME Do these a bit better...
+ if ($params->{site} eq 'emptyhomes' && $period eq '4 weeks') {
+ $q_params->{'(select max(whensent) from questionnaire where me.id=problem_id)'} = undef;
+ } elsif ($params->{site} eq 'emptyhomes' && $period eq '26 weeks') {
+ $q_params->{'(select max(whensent) from questionnaire where me.id=problem_id)'} = { '!=', undef };
+ } else {
+ $q_params->{'-or'} = [
+ '(select max(whensent) from questionnaire where me.id=problem_id)' => undef,
+ '(select max(whenanswered) from questionnaire where me.id=problem_id)' => { '<', \"ms_current_timestamp() - '$period'::interval" }
+ ];
+ }
+
+ my $unsent = FixMyStreet::App->model('DB::Problem')->search( $q_params, {
+ order_by => { -desc => 'confirmed' }
+ } );
+
+ while (my $row = $unsent->next) {
+
+ my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($row->cobrand)->new();
+ $cobrand->set_lang_and_domain($row->lang, 1);
+
+ # Cobranded and non-cobranded messages can share a database. In this case, the conf file
+ # should specify a vhost to send the reports for each cobrand, so that they don't get sent
+ # more than once if there are multiple vhosts running off the same database. The email_host
+ # call checks if this is the host that sends mail for this cobrand.
+ next unless $cobrand->email_host;
+
+ my $template;
+ if ($params->{site} eq 'emptyhomes') {
+ ($template = $period) =~ s/ //;
+ $template = File::Slurp::read_file( FixMyStreet->path_to( "templates/email/emptyhomes/" . $row->lang . "/questionnaire-$template.txt" )->stringify );
+ } else {
+ $template = File::Slurp::read_file( FixMyStreet->path_to( "templates/email/" . $cobrand->moniker . "/questionnaire.txt" )->stringify );
+ }
+
+ my %h = map { $_ => $row->$_ } qw/name title detail category/;
+ $h{created} = Utils::prettify_duration( time() - $row->confirmed->epoch, 'week' );
+
+ my $questionnaire = FixMyStreet::App->model('DB::Questionnaire')->create( {
+ problem_id => $row->id,
+ whensent => \'ms_current_timestamp()',
+ } );
+
+ # We won't send another questionnaire unless they ask for it (or it was
+ # the first EHA questionnaire.
+ $row->send_questionnaire( 0 )
+ if $params->{site} ne 'emptyhomes' || $period eq '26 weeks';
+
+ my $token = FixMyStreet::App->model("DB::Token")->new_result( {
+ scope => 'questionnaire',
+ data => $questionnaire->id,
+ } );
+ $h{url} = $cobrand->base_url_for_emails($row->cobrand_data) . '/Q/' . $token->token;
+
+ my $sender = $cobrand->contact_email;
+ my $sender_name = _($cobrand->contact_name);
+ $sender =~ s/team/fms-DO-NOT-REPLY/;
+
+ print "Sending questionnaire " . $questionnaire->id . ", problem "
+ . $row->id . ", token " . $token->token . " to "
+ . $row->user->email . "\n"
+ if $params->{verbose};
+
+ my $result = FixMyStreet::App->send_email_cron(
+ {
+ _template_ => $template,
+ _parameters_ => \%h,
+ To => [ [ $row->user->email, $row->name ] ],
+ From => [ $sender, $sender_name ],
+ },
+ $sender,
+ [ $row->user->email ],
+ $params->{nomail}
+ );
+ if ($result == mySociety::EmailUtil::EMAIL_SUCCESS) {
+ print " ...success\n" if $params->{verbose};
+ $row->update();
+ $token->insert();
+ } else {
+ print " ...failed\n" if $params->{verbose};
+ $questionnaire->delete;
+ }
+ }
+}
+
+sub timeline {
+ my ( $rs, $restriction ) = @_;
+
+ return $rs->search(
+ {
+ -or => {
+ whenanswered => { '>=', \"ms_current_timestamp()-'7 days'::interval" },
+ 'me.whensent' => { '>=', \"ms_current_timestamp()-'7 days'::interval" },
+ },
+ %{ $restriction },
+ },
+ {
+ -select => [qw/me.*/],
+ prefetch => [qw/problem/],
+ }
+ );
+}
+
+sub summary_count {
+ my ( $rs, $restriction ) = @_;
+
+ return $rs->search(
+ $restriction,
+ {
+ group_by => [ \'whenanswered is not null' ],
+ select => [ \'(whenanswered is not null)', { count => 'me.id' } ],
+ as => [qw/answered questionnaire_count/],
+ join => 'problem'
+ }
+ );
+}
+1;
diff --git a/perllib/FixMyStreet/DB/ResultSet/User.pm b/perllib/FixMyStreet/DB/ResultSet/User.pm
new file mode 100644
index 000000000..7e657a936
--- /dev/null
+++ b/perllib/FixMyStreet/DB/ResultSet/User.pm
@@ -0,0 +1,8 @@
+package FixMyStreet::DB::ResultSet::User;
+use base 'DBIx::Class::ResultSet';
+
+use strict;
+use warnings;
+
+
+1;
diff --git a/perllib/FixMyStreet/Geocode.pm b/perllib/FixMyStreet/Geocode.pm
index c06c3bb55..a520a2daa 100644
--- a/perllib/FixMyStreet/Geocode.pm
+++ b/perllib/FixMyStreet/Geocode.pm
@@ -17,8 +17,6 @@ use LWP::Simple;
use Digest::MD5 qw(md5_hex);
use URI::Escape;
-use Cobrand;
-use Page;
use Utils;
use mySociety::Config;
use mySociety::Locale;
@@ -38,14 +36,14 @@ BEGIN {
# Northern Ireland). The information in the query may be used by cobranded versions
# of the site to diambiguate locations.
sub lookup {
- my ($s, $q) = @_;
+ my ($s, $c) = @_;
my ($latitude, $longitude, $error);
if (mySociety::Config::get('COUNTRY') eq 'GB') {
if ($s =~ /^\d+$/) {
$error = 'FixMyStreet is a UK-based website that currently works in England, Scotland, and Wales. Please enter either a postcode, or a Great British street name and area.';
} elsif (mySociety::PostcodeUtil::is_valid_postcode($s)) {
my $location = mySociety::MaPit::call('postcode', $s);
- unless ($error = Page::mapit_check_error($location)) {
+ unless ($error = mapit_check_error($location)) {
$latitude = $location->{wgs84_lat};
$longitude = $location->{wgs84_lon};
}
@@ -53,20 +51,20 @@ sub lookup {
} elsif (mySociety::Config::get('COUNTRY') eq 'NO') {
if ($s =~ /^\d{4}$/) {
my $location = mySociety::MaPit::call('postcode', $s);
- unless ($error = Page::mapit_check_error($location)) {
+ unless ($error = mapit_check_error($location)) {
$latitude = $location->{wgs84_lat};
$longitude = $location->{wgs84_lon};
}
}
}
unless ($error || defined $latitude) {
- ($latitude, $longitude, $error) = FixMyStreet::Geocode::string($s, $q);
+ ($latitude, $longitude, $error) = FixMyStreet::Geocode::string($s, $c);
}
return ($latitude, $longitude, $error);
}
sub geocoded_string_coordinates {
- my ($js, $q) = @_;
+ my ( $js ) = @_;
my ($latitude, $longitude, $error);
my ($accuracy) = $js =~ /"Accuracy" *: *(\d)/;
if ($accuracy < 4) {
@@ -89,18 +87,18 @@ sub geocoded_string_coordinates {
}
sub results_check {
- my $q = shift;
+ my $c = shift;
my ($error, @valid_locations);
foreach (@_) {
next unless /"address" *: *"(.*?)"/s;
my $address = $1;
- next unless Cobrand::geocoded_string_check(Page::get_cobrand($q), $address, $q);
+ next unless $c->cobrand->geocoded_string_check( $address );
next if $address =~ /BT\d/;
push (@$error, $address);
push (@valid_locations, $_);
}
if (scalar @valid_locations == 1) {
- return geocoded_string_coordinates($valid_locations[0], $q);
+ return geocoded_string_coordinates( $valid_locations[0] );
}
$error = _('Sorry, we could not find that location.') unless $error;
return (undef, undef, $error);
@@ -112,12 +110,12 @@ sub results_check {
# an array of matches if there are more than one. The information in the query
# may be used to disambiguate the location in cobranded versions of the site.
sub string {
- my ($s, $q) = @_;
+ my ($s, $c) = @_;
$s = lc($s);
$s =~ s/[^-&\w ']/ /g;
$s =~ s/\s+/ /g;
$s = URI::Escape::uri_escape_utf8($s);
- $s = Cobrand::disambiguate_location(Page::get_cobrand($q), "q=$s", $q);
+ $s = $c->cobrand->disambiguate_location( "q=$s" );
$s =~ s/%20/+/g;
my $url = 'http://maps.google.com/maps/geo?' . $s;
my $cache_dir = mySociety::Config::get('GEO_CACHE');
@@ -126,8 +124,20 @@ sub string {
if (-s $cache_file) {
$js = File::Slurp::read_file($cache_file);
} else {
- $url .= ',+UK' unless $url =~ /united\++kingdom$/ || $url =~ /uk$/i
- || mySociety::Config::get('COUNTRY') ne 'GB';
+ # For some reason adding gl=uk is no longer sufficient to make google
+ # think we are in the UK for some locations so we explictly add UK to
+ # the address. We do it here so as not to invalidate existing cache
+ # entries
+ if ( mySociety::Config::get('COUNTRY') eq 'GB'
+ && $url !~ /,\+UK/
+ && $url !~ /united\++kingdom$/ )
+ {
+ if ( $url =~ /&/ ) {
+ $url =~ s/&/,+UK&/;
+ } else {
+ $url .= ',+UK';
+ }
+ }
$url .= '&sensor=false&key=' . mySociety::Config::get('GOOGLE_MAPS_API_KEY');
$js = LWP::Simple::get($url);
$js = encode_utf8($js) if utf8::is_utf8($js);
@@ -139,42 +149,32 @@ sub string {
} elsif ($js !~ /"code" *: *200/) {
$error = _('Sorry, we could not find that location.');
} elsif ($js =~ /}, *{/) { # Multiple
- return results_check($q, (split /}, *{/, $js));
+ return results_check($c, (split /}, *{/, $js));
} elsif ($js =~ /BT\d/) {
# Northern Ireland, hopefully
$error = _("We do not cover Northern Ireland, I'm afraid, as our licence doesn't include any maps for the region.");
} else {
- return results_check($q, $js);
+ return results_check($c, $js);
}
return (undef, undef, $error);
}
-# list_choices
-# Prints response if there's more than one possible result
-sub list_choices {
- my ($choices, $page, $q) = @_;
- my $url;
- my $cobrand = Page::get_cobrand($q);
- my $message = _('We found more than one match for that location. We show up to ten matches, please try a different search if yours is not here.');
- my $out = '<p>' . $message . '</p>';
- my $choice_list = '<ul>';
- foreach my $choice (@$choices) {
- $choice = decode_utf8($choice);
- $choice =~ s/, United Kingdom//;
- $choice =~ s/, UK//;
- $url = Cobrand::url($cobrand, NewURL($q, -retain => 1, -url => $page, 'pc' => $choice), $q);
- $url =~ s/%20/+/g;
- $choice_list .= '<li><a href="' . $url . '">' . $choice . "</a></li>\n";
+sub mapit_check_error {
+ my $location = shift;
+ if ($location->{error}) {
+ return _('That postcode was not recognised, sorry.') if $location->{code} =~ /^4/;
+ return $location->{error};
+ }
+ if (mySociety::Config::get('COUNTRY') eq 'GB') {
+ my $island = $location->{coordsyst};
+ if (!$island) {
+ return _("Sorry, that appears to be a Crown dependency postcode, which we don't cover.");
+ }
+ if ($island eq 'I') {
+ return _("We do not cover Northern Ireland, I'm afraid, as our licence doesn't include any maps for the region.");
+ }
}
- $choice_list .= '</ul>';
- $out .= $choice_list;
- my %vars = (message => $message,
- choice_list => $choice_list,
- header => _('More than one match'),
- url_home => Cobrand::url($cobrand, '/', $q));
- my $cobrand_choice = Page::template_include('geocode-choice', $q, Page::template_root($q), %vars);
- return $cobrand_choice if $cobrand_choice;
- return $out;
+ return 0;
}
1;
diff --git a/perllib/FixMyStreet/Map.pm b/perllib/FixMyStreet/Map.pm
index 5305b360a..125aca9e6 100644
--- a/perllib/FixMyStreet/Map.pm
+++ b/perllib/FixMyStreet/Map.pm
@@ -19,8 +19,6 @@ use Module::Pluggable
# Get the list of maps we want and load map classes at compile time
my @ALL_MAP_CLASSES = allowed_maps();
-use Problems;
-use Cobrand;
use mySociety::Config;
use mySociety::Gaze;
use mySociety::Locale;
@@ -63,36 +61,8 @@ sub display_map {
return $map_class->display_map(@_);
}
-sub display_map_end {
- my ($type) = @_;
- my $out = '</div>';
- $out .= '</form>' if ($type);
- return $out;
-}
-
-sub header {
- my ( $q, $type ) = @_;
- return '' unless $type;
-
- my $cobrand = Page::get_cobrand($q);
- my $cobrand_form_elements =
- Cobrand::form_elements( $cobrand, 'mapForm', $q );
- my $form_action = Cobrand::url( $cobrand, '/', $q );
- my $encoding = '';
- $encoding = ' enctype="multipart/form-data"' if $type == 2;
- my $pc = ent($q->param('pc') || '');
- my $map = ent($q->param('map') || '');
- return <<EOF;
-<form action="$form_action" method="post" name="mapForm" id="mapForm"$encoding>
-<input type="hidden" name="submit_map" value="1">
-<input type="hidden" name="map" value="$map">
-<input type="hidden" name="pc" value="$pc">
-$cobrand_form_elements
-EOF
-}
-
sub map_features {
- my ( $q, $lat, $lon, $interval ) = @_;
+ my ( $c, $lat, $lon, $interval ) = @_;
# TODO - be smarter about calculating the surrounding square
# use deltas that are roughly 500m in the UK - so we get a 1 sq km search box
@@ -106,12 +76,11 @@ sub map_features {
my $max_lon = $lon + $lon_delta;
# list of problems around map can be limited, but should show all pins
- my $around_limit #
- = Cobrand::on_map_list_limit( Page::get_cobrand($q) ) || undef;
+ my $around_limit = $c->cobrand->on_map_list_limit || undef;
my @around_args = ( $min_lat, $max_lat, $min_lon, $max_lon, $interval );
- my $around_map_list = Problems::around_map( @around_args, $around_limit );
- my $around_map = Problems::around_map( @around_args, undef );
+ my $around_map_list = $c->cobrand->problems->around_map( @around_args, $around_limit );
+ my $around_map = $c->cobrand->problems->around_map( @around_args, undef );
my $dist;
mySociety::Locale::in_gb_locale {
@@ -122,9 +91,10 @@ sub map_features {
$dist = int( $dist * 10 + 0.5 ) / 10;
my $limit = 20;
- my @ids = map { $_->{id} } @$around_map_list;
- my $nearby = Problems::nearby( $dist, join( ',', @ids ),
- $limit, $lat, $lon, $interval );
+ my @ids = map { $_->id } @$around_map_list;
+ my $nearby = $c->model('DB::Nearby')->nearby(
+ $c, $dist, \@ids, $limit, $lat, $lon, $interval
+ );
return ( $around_map, $around_map_list, $nearby, $dist );
}
diff --git a/perllib/FixMyStreet/Map/Bing.pm b/perllib/FixMyStreet/Map/Bing.pm
index 335759b08..54979eba1 100644
--- a/perllib/FixMyStreet/Map/Bing.pm
+++ b/perllib/FixMyStreet/Map/Bing.pm
@@ -11,55 +11,18 @@ package FixMyStreet::Map::Bing;
use strict;
use mySociety::Web qw(ent);
-sub header_js {
- return '
-<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&mkt=en-GB"></script>
-<script type="text/javascript" src="/js/map-bing.js"></script>
-';
-}
-
-# display_map Q PARAMS
+# display_map C PARAMS
# PARAMS include:
-# EASTING, NORTHING for the centre point of the map
-# TYPE is 1 if the map is clickable, 2 if clickable and has a form upload,
-# 0 if not clickable
+# latitude, longitude for the centre point of the map
+# CLICKABLE is set if the map is clickable
# PINS is array of pins to show, location and colour
-# PRE/POST are HTML to show above/below map
sub display_map {
- my ($self, $q, %params) = @_;
- $params{pre} ||= '';
- $params{post} ||= '';
-
- my @pins;
- foreach my $pin (@{$params{pins}}) {
- $pin->[3] ||= '';
- push @pins, "[ $pin->[0], $pin->[1], '$pin->[2]', '$pin->[3]' ]";
- }
- my $pins_js = join(",\n", @pins);
-
- my $out = FixMyStreet::Map::header($q, $params{type});
- my $copyright = _('Map contains Ordnance Survey data &copy; Crown copyright and database right 2010.');
- my $key = mySociety::Config::get('BING_MAPS_API_KEY');
- $out .= <<EOF;
-<input type="hidden" name="latitude" id="fixmystreet.latitude" value="$params{latitude}">
-<input type="hidden" name="longitude" id="fixmystreet.longitude" value="$params{longitude}">
-<script type="text/javascript">
-var fixmystreet = {
- 'key': '$key',
- 'latitude': $params{latitude},
- 'longitude': $params{longitude},
- 'pins': [ $pins_js ]
-}
-</script>
-<div id="map_box">
- $params{pre}
- <div id="map"></div>
- <p id="copyright">$copyright</p>
- $params{post}
-</div>
-<div id="side">
-EOF
- return $out;
+ my ($self, $c, %params) = @_;
+ $c->stash->{map} = {
+ %params,
+ type => 'bing',
+ key => mySociety::Config::get('BING_MAPS_API_KEY'),
+ };
}
1;
diff --git a/perllib/FixMyStreet/Map/BingOL.pm b/perllib/FixMyStreet/Map/BingOL.pm
deleted file mode 100644
index 968642807..000000000
--- a/perllib/FixMyStreet/Map/BingOL.pm
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/perl
-#
-# FixMyStreet:Map::BingOL
-# Bing maps on FixMyStreet, using OpenLayers.
-#
-# Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/
-
-package FixMyStreet::Map::BingOL;
-
-use strict;
-use mySociety::Gaze;
-use mySociety::Web qw(ent);
-
-sub header_js {
- return '
-<!-- <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&mkt=en-GB"></script> -->
-<script type="text/javascript" src="/jslib/OpenLayers-2.10/OpenLayers.js"></script>
-<script type="text/javascript" src="/js/map-OpenLayers.js"></script>
-<script type="text/javascript" src="/js/map-bing-ol.js"></script>
-';
-}
-
-# display_map Q PARAMS
-# PARAMS include:
-# EASTING, NORTHING for the centre point of the map
-# TYPE is 1 if the map is clickable, 2 if clickable and has a form upload,
-# 0 if not clickable
-# PINS is array of pins to show, location and colour
-# PRE/POST are HTML to show above/below map
-sub display_map {
- my ($self, $q, %params) = @_;
- $params{pre} ||= '';
- $params{post} ||= '';
-
- my @pins;
- foreach my $pin (@{$params{pins}}) {
- $pin->[3] ||= '';
- push @pins, "[ $pin->[0], $pin->[1], '$pin->[2]', '$pin->[3]' ]";
- }
- my $pins_js = join(",\n", @pins);
-
- my $out = FixMyStreet::Map::header($q, $params{type});
- my $copyright = _('Map contains Ordnance Survey data &copy; Crown copyright and database right 2010. Microsoft');
- my $dist = mySociety::Gaze::get_radius_containing_population( $params{latitude}, $params{longitude}, 200_000 );
- my $zoom = 2;
- $zoom = 3 if $dist < 10;
- $out .= <<EOF;
-<input type="hidden" name="latitude" id="fixmystreet.latitude" value="$params{latitude}">
-<input type="hidden" name="longitude" id="fixmystreet.longitude" value="$params{longitude}">
-<script type="text/javascript">
-var fixmystreet = {
- 'latitude': $params{latitude},
- 'longitude': $params{longitude},
- 'zoom': $zoom,
- 'pins': [ $pins_js ]
-}
-</script>
-<div id="map_box">
- $params{pre}
- <div id="map"></div>
- <p id="copyright">$copyright</p>
- $params{post}
-</div>
-<div id="side">
-EOF
- return $out;
-}
-
-1;
diff --git a/perllib/FixMyStreet/Map/FMS.pm b/perllib/FixMyStreet/Map/FMS.pm
new file mode 100644
index 000000000..d0a28be9b
--- /dev/null
+++ b/perllib/FixMyStreet/Map/FMS.pm
@@ -0,0 +1,62 @@
+#!/usr/bin/perl
+#
+# FixMyStreet:Map::FMS
+# Bing and OS StreetView maps on FixMyStreet, using OpenLayers.
+#
+# Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved.
+# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/
+
+package FixMyStreet::Map::FMS;
+use base 'FixMyStreet::Map::OSM';
+
+use strict;
+
+use constant ZOOM_LEVELS => 5;
+use constant MIN_ZOOM_LEVEL => 13;
+
+# Is set by the JavaScript
+sub map_type {
+ return '""';
+}
+
+sub map_template {
+ return 'fms';
+}
+
+sub copyright {
+ return _('Map contains Ordnance Survey data &copy; Crown copyright and database right 2010. Microsoft');
+}
+
+sub get_quadkey {
+ my ($x, $y, $z) = @_;
+ my $key = '';
+ for (my $i = $z; $i > 0; $i--) {
+ my $digit = 0;
+ my $mask = 1 << ($i - 1);
+ $digit++ if ($x & $mask) != 0;
+ $digit += 2 if ($y & $mask) != 0;
+ $key .= $digit;
+ }
+ return $key;
+}
+
+sub map_tiles {
+ my ($self, $x, $y, $z) = @_;
+ if ($z >= 16) {
+ return [
+ "http://a.os.openstreetmap.org/sv/$z/" . ($x-1) . "/" . ($y-1) . ".png",
+ "http://b.os.openstreetmap.org/sv/$z/$x/" . ($y-1) . ".png",
+ "http://c.os.openstreetmap.org/sv/$z/" . ($x-1) . "/$y.png",
+ "http://os.openstreetmap.org/sv/$z/$x/$y.png",
+ ];
+ } else {
+ return [
+ "http://ecn.t0.tiles.virtualearth.net/tiles/r" . get_quadkey($x-1, $y-1, $z) . ".png?g=587&productSet=mmOS",
+ "http://ecn.t1.tiles.virtualearth.net/tiles/r" . get_quadkey($x, $y-1, $z) . ".png?g=587&productSet=mmOS",
+ "http://ecn.t2.tiles.virtualearth.net/tiles/r" . get_quadkey($x-1, $y, $z) . ".png?g=587&productSet=mmOS",
+ "http://ecn.t3.tiles.virtualearth.net/tiles/r" . get_quadkey($x, $y, $z) . ".png?g=587&productSet=mmOS",
+ ];
+ }
+}
+
+1;
diff --git a/perllib/FixMyStreet/Map/Google.pm b/perllib/FixMyStreet/Map/Google.pm
index 35896108b..ceb3a53ed 100644
--- a/perllib/FixMyStreet/Map/Google.pm
+++ b/perllib/FixMyStreet/Map/Google.pm
@@ -11,53 +11,17 @@ package FixMyStreet::Map::Google;
use strict;
use mySociety::Web qw(ent);
-sub header_js {
- return '
-<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
-<script type="text/javascript" src="/js/map-google.js"></script>
-';
-}
-
-# display_map Q PARAMS
+# display_map C PARAMS
# PARAMS include:
-# EASTING, NORTHING for the centre point of the map
-# TYPE is 1 if the map is clickable, 2 if clickable and has a form upload,
-# 0 if not clickable
+# latitude, longitude for the centre point of the map
+# CLICKABLE is set if the map is clickable
# PINS is array of pins to show, location and colour
-# PRE/POST are HTML to show above/below map
sub display_map {
- my ($self, $q, %params) = @_;
- $params{pre} ||= '';
- $params{post} ||= '';
-
- my @pins;
- foreach my $pin (@{$params{pins}}) {
- $pin->[3] ||= '';
- push @pins, "[ $pin->[0], $pin->[1], '$pin->[2]', '$pin->[3]' ]";
- }
- my $pins_js = join(",\n", @pins);
-
- my $out = FixMyStreet::Map::header($q, $params{type});
- my $copyright = _('Map contains Ordnance Survey data &copy; Crown copyright and database right 2010.');
- $out .= <<EOF;
-<input type="hidden" name="latitude" id="fixmystreet.latitude" value="$params{latitude}">
-<input type="hidden" name="longitude" id="fixmystreet.longitude" value="$params{longitude}">
-<script type="text/javascript">
-var fixmystreet = {
- 'latitude': $params{latitude},
- 'longitude': $params{longitude},
- 'pins': [ $pins_js ]
-}
-</script>
-<div id="map_box">
- $params{pre}
- <div id="map"></div>
- <p id="copyright">$copyright</p>
- $params{post}
-</div>
-<div id="side">
-EOF
- return $out;
+ my ($self, $c, %params) = @_;
+ $c->stash->{map} = {
+ %params,
+ type => 'google',
+ };
}
1;
diff --git a/perllib/FixMyStreet/Map/OSM.pm b/perllib/FixMyStreet/Map/OSM.pm
index b930a4e4d..4543ce2da 100644
--- a/perllib/FixMyStreet/Map/OSM.pm
+++ b/perllib/FixMyStreet/Map/OSM.pm
@@ -10,116 +10,92 @@ package FixMyStreet::Map::OSM;
use strict;
use Math::Trig;
-use mySociety::Web qw(ent NewURL);
+use mySociety::Gaze;
use Utils;
-sub header_js {
- return '
-<script type="text/javascript" src="/jslib/OpenLayers-2.10/OpenLayers.js"></script>
-<script type="text/javascript" src="/js/map-OpenLayers.js"></script>
-<script type="text/javascript" src="/js/map-OpenStreetMap.js"></script>
-';
-}
+use constant ZOOM_LEVELS => 5;
+use constant MIN_ZOOM_LEVEL => 13;
sub map_type {
return 'OpenLayers.Layer.OSM.Mapnik';
}
-# display_map Q PARAMS
+sub map_template {
+ return 'osm';
+}
+
+sub map_tiles {
+ my ($self, $x, $y, $z) = @_;
+ my $tile_url = $self->base_tile_url();
+ return [
+ "http://a.$tile_url/$z/" . ($x - 1) . "/" . ($y - 1) . ".png",
+ "http://b.$tile_url/$z/$x/" . ($y - 1) . ".png",
+ "http://c.$tile_url/$z/" . ($x - 1) . "/$y.png",
+ "http://$tile_url/$z/$x/$y.png",
+ ];
+}
+
+sub base_tile_url {
+ return 'tile.openstreetmap.org';
+}
+
+sub copyright {
+ return _('Map &copy; <a id="osm_link" href="http://www.openstreetmap.org/">OpenStreetMap</a> and contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>');
+}
+
+# display_map C PARAMS
# PARAMS include:
# latitude, longitude for the centre point of the map
-# TYPE is 1 if the map is clickable, 2 if clickable and has a form upload,
-# 0 if not clickable
+# CLICKABLE is set if the map is clickable
# PINS is array of pins to show, location and colour
-# PRE/POST are HTML to show above/below map
sub display_map {
- my ($self, $q, %params) = @_;
- $params{pre} ||= '';
- $params{post} ||= '';
+ my ($self, $c, %params) = @_;
- # Map centre may be overridden in the query string
- $params{latitude} = Utils::truncate_coordinate($q->param('lat')+0)
- if defined $q->param('lat');
- $params{longitude} = Utils::truncate_coordinate($q->param('lon')+0)
- if defined $q->param('lon');
+ # Adjust zoom level dependent upon population density
+ my $dist = mySociety::Gaze::get_radius_containing_population( $params{latitude}, $params{longitude}, 200_000 );
+ my $default_zoom = ZOOM_LEVELS - 3;
+ $default_zoom = ZOOM_LEVELS - 2 if $dist < 10;
- my $zoom = defined $q->param('zoom') ? $q->param('zoom') : 2;
- my $zoom_act = 14 + $zoom;
+ # Map centre may be overridden in the query string
+ $params{latitude} = Utils::truncate_coordinate($c->req->params->{lat} + 0)
+ if defined $c->req->params->{lat};
+ $params{longitude} = Utils::truncate_coordinate($c->req->params->{lon} + 0)
+ if defined $c->req->params->{lon};
+
+ my $zoom = defined $c->req->params->{zoom} ? $c->req->params->{zoom} + 0 : $default_zoom;
+ $zoom = ZOOM_LEVELS - 1 if $zoom >= ZOOM_LEVELS;
+ $zoom = 0 if $zoom < 0;
+ my $zoom_act = MIN_ZOOM_LEVEL + $zoom;
my ($x_tile, $y_tile) = latlon_to_tile_with_adjust($params{latitude}, $params{longitude}, $zoom_act);
- my $tl = ($x_tile-1) . "/" . ($y_tile-1);
- my $tr = "$x_tile/" . ($y_tile-1);
- my $bl = ($x_tile-1) . "/$y_tile";
- my $br = "$x_tile/$y_tile";
- my $tl_src = "http://a.tile.openstreetmap.org/$zoom_act/$tl.png";
- my $tr_src = "http://b.tile.openstreetmap.org/$zoom_act/$tr.png";
- my $bl_src = "http://c.tile.openstreetmap.org/$zoom_act/$bl.png";
- my $br_src = "http://tile.openstreetmap.org/$zoom_act/$br.png";
- map { s{/}{.} } ($tl, $tr, $bl, $br);
-
- my @pins;
- my $pins = '';
foreach my $pin (@{$params{pins}}) {
- $pin->[3] ||= '';
- push @pins, "[ $pin->[0], $pin->[1], '$pin->[2]', '$pin->[3]' ]";
- $pins .= display_pin($q, $pin, $x_tile, $y_tile, $zoom_act);
+ ($pin->{px}, $pin->{py}) = latlon_to_px($pin->{latitude}, $pin->{longitude}, $x_tile, $y_tile, $zoom_act);
}
- my $pins_js = join(",\n", @pins);
- my $img_type;
- if ($params{type}) {
- $img_type = '<input type="image"';
- } else {
- $img_type = '<img';
- }
- my $out = FixMyStreet::Map::header($q, $params{type});
- my $copyright = _('Map &copy; <a id="osm_link" href="http://www.openstreetmap.org/">OpenStreetMap</a> and contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>');
- my $compass = compass($q, $x_tile, $y_tile, $zoom);
- my $map_type = $self->map_type();
- $out .= <<EOF;
-<input type="hidden" name="latitude" id="fixmystreet.latitude" value="$params{latitude}">
-<input type="hidden" name="longitude" id="fixmystreet.longitude" value="$params{longitude}">
-<input type="hidden" name="zoom" value="$zoom">
-<script type="text/javascript">
-var fixmystreet = {
- 'latitude': $params{latitude},
- 'longitude': $params{longitude},
- 'pins': [ $pins_js ],
- 'map_type': $map_type
-}
-</script>
-<div id="map_box">
- $params{pre}
- <div id="map"><noscript>
- <div id="drag">$img_type alt="NW map tile" id="t2.2" name="tile_$tl" src="$tl_src" style="top:0; left:0;">$img_type alt="NE map tile" id="t2.3" name="tile_$tr" src="$tr_src" style="top:0px; left:256px;"><br>$img_type alt="SW map tile" id="t3.2" name="tile_$bl" src="$bl_src" style="top:256px; left:0;">$img_type alt="SE map tile" id="t3.3" name="tile_$br" src="$br_src" style="top:256px; left:256px;"></div>
- <div id="pins">$pins</div>
- $compass
- </noscript></div>
- <p id="copyright">$copyright</p>
- $params{post}
-</div>
-<div id="side">
-EOF
- return $out;
+ $c->stash->{map} = {
+ %params,
+ type => $self->map_template(),
+ map_type => $self->map_type(),
+ tiles => $self->map_tiles( $x_tile, $y_tile, $zoom_act ),
+ copyright => $self->copyright(),
+ x_tile => $x_tile,
+ y_tile => $y_tile,
+ zoom => $zoom,
+ zoom_act => $zoom_act,
+ zoom_levels => ZOOM_LEVELS,
+ compass => compass( $x_tile, $y_tile, $zoom_act ),
+ };
}
-sub display_pin {
- my ($q, $pin, $x_tile, $y_tile, $zoom) = @_;
-
- my ($px, $py) = latlon_to_px($pin->[0], $pin->[1], $x_tile, $y_tile, $zoom);
-
- my $num = '';
- my $host = Page::base_url_with_lang($q, undef);
- my %cols = (red=>'R', green=>'G', blue=>'B', purple=>'P');
- my $out = '<img border="0" class="pin" src="' . $host . '/i/pin' . $cols{$pin->[2]}
- . $num . '.gif" alt="' . _('Problem') . '" style="top:' . ($py-59)
- . 'px; left:' . ($px) . 'px; position: absolute;">';
- return $out unless $pin->[3];
- my $cobrand = Page::get_cobrand($q);
- my $url = Cobrand::url($cobrand, NewURL($q, -url => '/report/' . $pin->[3]), $q);
- # XXX Would like to include title here in title=""
- $out = '<a href="' . $url . '">' . $out . '</a>';
- return $out;
+sub compass {
+ my ( $x, $y, $z ) = @_;
+ return {
+ north => [ map { Utils::truncate_coordinate($_) } tile_to_latlon( $x, $y-1, $z ) ],
+ south => [ map { Utils::truncate_coordinate($_) } tile_to_latlon( $x, $y+1, $z ) ],
+ west => [ map { Utils::truncate_coordinate($_) } tile_to_latlon( $x-1, $y, $z ) ],
+ east => [ map { Utils::truncate_coordinate($_) } tile_to_latlon( $x+1, $y, $z ) ],
+ here => [ map { Utils::truncate_coordinate($_) } tile_to_latlon( $x, $y, $z ) ],
+ };
}
# Given a lat/lon, convert it to OSM tile co-ordinates (precise).
@@ -183,43 +159,12 @@ sub click_to_tile {
# Given some click co-ords (the tile they were on, and where in the
# tile they were), convert to WGS84 and return.
sub click_to_wgs84 {
- my ($self, $q, $pin_tile_x, $pin_x, $pin_tile_y, $pin_y) = @_;
+ my ($self, $c, $pin_tile_x, $pin_x, $pin_tile_y, $pin_y) = @_;
my $tile_x = click_to_tile($pin_tile_x, $pin_x);
my $tile_y = click_to_tile($pin_tile_y, $pin_y);
- my $zoom = 14 + (defined $q->param('zoom') ? $q->param('zoom') : 2);
+ my $zoom = MIN_ZOOM_LEVEL + (defined $c->req->params->{zoom} ? $c->req->params->{zoom} : 3);
my ($lat, $lon) = tile_to_latlon($tile_x, $tile_y, $zoom);
return ( $lat, $lon );
}
-sub compass ($$$$) {
- my ( $q, $x, $y, $zoom ) = @_;
-
- my ($lat, $lon) = map { Utils::truncate_coordinate($_) } tile_to_latlon($x, $y-1, $zoom+14);
- my $north = NewURL( $q, lat => $lat, lon => $lon, zoom => $zoom );
- ($lat, $lon) = map { Utils::truncate_coordinate($_) } tile_to_latlon($x, $y+1, $zoom+14);
- my $south = NewURL( $q, lat => $lat, lon => $lon, zoom => $zoom );
- ($lat, $lon) = map { Utils::truncate_coordinate($_) } tile_to_latlon($x-1, $y, $zoom+14);
- my $west = NewURL( $q, lat => $lat, lon => $lon, zoom => $zoom );
- ($lat, $lon) = map { Utils::truncate_coordinate($_) } tile_to_latlon($x+1, $y, $zoom+14);
- my $east = NewURL( $q, lat => $lat, lon => $lon, zoom => $zoom );
- ($lat, $lon) = map { Utils::truncate_coordinate($_) } tile_to_latlon($x, $y, $zoom+14);
- my $zoom_in = $zoom < 3 ? NewURL( $q, lat => $lat, lon => $lon, zoom => $zoom+1 ) : '#';
- my $zoom_out = $zoom > 0 ? NewURL( $q, lat => $lat, lon => $lon, zoom => $zoom-1 ) : '#';
- my $world = NewURL( $q, lat => $lat, lon => $lon, zoom => 0 );
-
- #my $host = Page::base_url_with_lang( $q, undef );
- my $dir = "/jslib/OpenLayers-2.10/img";
- return <<EOF;
-<div style="position: absolute; left: 4px; top: 4px; z-index: 1007;" class="olControlPanZoom olControlNoSelect" unselectable="on">
- <div style="position: absolute; left: 13px; top: 4px; width: 18px; height: 18px;"><a href="$north"><img style="position: relative; width: 18px; height: 18px;" src="$dir/north-mini.png" border="0"></a></div>
- <div style="position: absolute; left: 4px; top: 22px; width: 18px; height: 18px;"><a href="$west"><img style="position: relative; width: 18px; height: 18px;" src="$dir/west-mini.png" border="0"></a></div>
- <div style="position: absolute; left: 22px; top: 22px; width: 18px; height: 18px;"><a href="$east"><img style="position: relative; width: 18px; height: 18px;" src="$dir/east-mini.png" border="0"></a></div>
- <div style="position: absolute; left: 13px; top: 40px; width: 18px; height: 18px;"><a href="$south"><img style="position: relative; width: 18px; height: 18px;" src="$dir/south-mini.png" border="0"></a></div>
- <div style="position: absolute; left: 13px; top: 63px; width: 18px; height: 18px;"><a href="$zoom_in"><img style="position: relative; width: 18px; height: 18px;" src="$dir/zoom-plus-mini.png" border="0"></a></div>
- <div style="position: absolute; left: 13px; top: 81px; width: 18px; height: 18px;"><a href="$world"><img style="position: relative; width: 18px; height: 18px;" src="$dir/zoom-world-mini.png" border="0"></a></div>
- <div style="position: absolute; left: 13px; top: 99px; width: 18px; height: 18px;"><a href="$zoom_out"><img style="position: relative; width: 18px; height: 18px;" src="$dir/zoom-minus-mini.png" border="0"></a></div>
-</div>
-EOF
-}
-
1;
diff --git a/perllib/FixMyStreet/Map/OSM/CycleMap.pm b/perllib/FixMyStreet/Map/OSM/CycleMap.pm
index 06b07ae20..71b86de8f 100644
--- a/perllib/FixMyStreet/Map/OSM/CycleMap.pm
+++ b/perllib/FixMyStreet/Map/OSM/CycleMap.pm
@@ -15,4 +15,8 @@ sub map_type {
return 'OpenLayers.Layer.OSM.CycleMap';
}
+sub base_tile_url {
+ return 'tile.opencyclemap.org/cycle';
+}
+
1;
diff --git a/perllib/FixMyStreet/Map/OSM/StreetView.pm b/perllib/FixMyStreet/Map/OSM/StreetView.pm
index 9c9a1ac8e..141c2e328 100644
--- a/perllib/FixMyStreet/Map/OSM/StreetView.pm
+++ b/perllib/FixMyStreet/Map/OSM/StreetView.pm
@@ -7,58 +7,24 @@
# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/
package FixMyStreet::Map::OSM::StreetView;
+use base 'FixMyStreet::Map::OSM';
use strict;
-use mySociety::Web qw(ent);
-sub header_js {
- return '
-<script type="text/javascript" src="/jslib/OpenLayers-2.10/OpenLayers.js"></script>
-<script type="text/javascript" src="/js/map-OpenLayers.js"></script>
-<script type="text/javascript" src="/js/map-streetview.js"></script>
-';
+sub map_type {
+ return '""';
}
-# display_map Q PARAMS
-# PARAMS include:
-# EASTING, NORTHING for the centre point of the map
-# TYPE is 1 if the map is clickable, 2 if clickable and has a form upload,
-# 0 if not clickable
-# PINS is array of pins to show, location and colour
-# PRE/POST are HTML to show above/below map
-sub display_map {
- my ($self, $q, %params) = @_;
- $params{pre} ||= '';
- $params{post} ||= '';
-
- my @pins;
- foreach my $pin (@{$params{pins}}) {
- $pin->[3] ||= '';
- push @pins, "[ $pin->[0], $pin->[1], '$pin->[2]', '$pin->[3]' ]";
- }
- my $pins_js = join(",\n", @pins);
+sub map_template {
+ return 'osm-streetview';
+}
- my $out = FixMyStreet::Map::header($q, $params{type});
- my $copyright = _('Map contains Ordnance Survey data &copy; Crown copyright and database right 2010.');
- $out .= <<EOF;
-<input type="hidden" name="latitude" id="fixmystreet.latitude" value="$params{latitude}">
-<input type="hidden" name="longitude" id="fixmystreet.longitude" value="$params{longitude}">
-<script type="text/javascript">
-var fixmystreet = {
- 'latitude': $params{latitude},
- 'longitude': $params{longitude},
- 'pins': [ $pins_js ]
+sub base_tile_url {
+ return 'os.openstreetmap.org/sv';
}
-</script>
-<div id="map_box">
- $params{pre}
- <div id="map"></div>
- <p id="copyright">$copyright</p>
- $params{post}
-</div>
-<div id="side">
-EOF
- return $out;
+
+sub copyright {
+ return _('Map contains Ordnance Survey data &copy; Crown copyright and database right 2010.');
}
1;
diff --git a/perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm b/perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm
index 9ae5829c4..34df8dc8b 100644
--- a/perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm
+++ b/perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm
@@ -7,73 +7,22 @@
# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/
package FixMyStreet::Map::Tilma::OL::1_10k;
+use base 'FixMyStreet::Map::Tilma::OpenLayers';
use strict;
-use constant TILE_WIDTH => 254;
-use constant TIF_SIZE_M => 5000;
-use constant TIF_SIZE_PX => 7874;
-use constant SCALE_FACTOR => TIF_SIZE_M / (TIF_SIZE_PX / TILE_WIDTH);
-use constant TILE_TYPE => '10k-full';
+sub tile_width { return 254; }
+sub tif_size_m { return 5000; }
+sub tif_size_px { return 7874; }
+sub scale_factor { return tif_size_m() / (tif_size_px() / tile_width()); }
+sub tile_type { return '10k-full'; }
-sub header_js {
- return '
-<script type="text/javascript" src="/jslib/OpenLayers-2.10/OpenLayers.js"></script>
-<script type="text/javascript" src="/js/map-OpenLayers.js"></script>
-<script type="text/javascript" src="/js/map-tilma-ol.js"></script>
-<script type="text/javascript" src="/js/OpenLayers.Projection.OrdnanceSurvey.js"></script>
-';
+sub copyright {
+ return _('&copy; Crown copyright. All rights reserved. Ministry of Justice 100037819&nbsp;2008.');
}
-# display_map Q PARAMS
-# PARAMS include:
-# EASTING, NORTHING for the centre point of the map
-# TYPE is 1 if the map is clickable, 2 if clickable and has a form upload,
-# 0 if not clickable
-# PINS is array of pins to show, location and colour
-# PRE/POST are HTML to show above/below map
-sub display_map {
- my ($self, $q, %params) = @_;
- $params{pre} ||= '';
- $params{post} ||= '';
-
- my @pins;
- foreach my $pin (@{$params{pins}}) {
- $pin->[3] ||= '';
- push @pins, "[ $pin->[0], $pin->[1], '$pin->[2]', '$pin->[3]' ]";
- }
- my $pins_js = join(",\n", @pins);
-
- my $out = FixMyStreet::Map::header($q, $params{type});
- my $tile_width = TILE_WIDTH;
- my $tile_type = TILE_TYPE;
- my $sf = SCALE_FACTOR / TILE_WIDTH;
- my $copyright = _('&copy; Crown copyright. All rights reserved. Ministry of Justice 100037819&nbsp;2008.');
- $out .= <<EOF;
-<input type="hidden" name="latitude" id="fixmystreet.latitude" value="$params{latitude}">
-<input type="hidden" name="longitude" id="fixmystreet.longitude" value="$params{longitude}">
-<script type="text/javascript">
-var fixmystreet = {
- 'tilewidth': $tile_width,
- 'tileheight': $tile_width,
- 'latitude': $params{latitude},
- 'longitude': $params{longitude},
- 'pins': [ $pins_js ],
- 'tile_type': '$tile_type',
- 'maxResolution': $sf
-};
-</script>
-<div id="map_box">
- $params{pre}
- <div id="map">
- <div id="watermark"></div>
- </div>
- <p id="copyright">$copyright</p>
-$params{post}
-</div>
-<div id="side">
-EOF
- return $out;
+sub watermark {
+ return 1;
}
1;
diff --git a/perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm b/perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm
index 7a898b55b..2a531766c 100644
--- a/perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm
+++ b/perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm
@@ -7,71 +7,22 @@
# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/
package FixMyStreet::Map::Tilma::OL::StreetView;
+use base 'FixMyStreet::Map::Tilma::OpenLayers';
use strict;
-use constant TILE_WIDTH => 250;
-use constant TIF_SIZE_M => 5000;
-use constant TIF_SIZE_PX => 5000;
-use constant SCALE_FACTOR => TIF_SIZE_M / (TIF_SIZE_PX / TILE_WIDTH);
-use constant TILE_TYPE => 'streetview';
+sub tile_width { return 250; }
+sub tif_size_m { return 5000; }
+sub tif_size_px { return 5000; }
+sub scale_factor { return tif_size_m() / (tif_size_px() / tile_width()); }
+sub tile_type { return 'streetview'; }
-sub header_js {
- return '
-<script type="text/javascript" src="/jslib/OpenLayers-2.10/OpenLayers.js"></script>
-<script type="text/javascript" src="/js/map-OpenLayers.js"></script>
-<script type="text/javascript" src="/js/map-tilma-ol.js"></script>
-<script type="text/javascript" src="/js/OpenLayers.Projection.OrdnanceSurvey.js"></script>
-';
+sub copyright {
+ return _('Map contains Ordnance Survey data &copy; Crown copyright and database right 2010.');
}
-# display_map Q PARAMS
-# PARAMS include:
-# EASTING, NORTHING for the centre point of the map
-# TYPE is 1 if the map is clickable, 2 if clickable and has a form upload,
-# 0 if not clickable
-# PINS is array of pins to show, location and colour
-# PRE/POST are HTML to show above/below map
-sub display_map {
- my ($self, $q, %params) = @_;
- $params{pre} ||= '';
- $params{post} ||= '';
-
- my @pins;
- foreach my $pin (@{$params{pins}}) {
- $pin->[3] ||= '';
- push @pins, "[ $pin->[0], $pin->[1], '$pin->[2]', '$pin->[3]' ]";
- }
- my $pins_js = join(",\n", @pins);
-
- my $out = FixMyStreet::Map::header($q, $params{type});
- my $tile_width = TILE_WIDTH;
- my $tile_type = TILE_TYPE;
- my $sf = SCALE_FACTOR / TILE_WIDTH;
- my $copyright = _('Map contains Ordnance Survey data &copy; Crown copyright and database right 2010.');
- $out .= <<EOF;
-<input type="hidden" name="latitude" id="fixmystreet.latitude" value="$params{latitude}">
-<input type="hidden" name="longitude" id="fixmystreet.longitude" value="$params{longitude}">
-<script type="text/javascript">
-var fixmystreet = {
- 'tilewidth': $tile_width,
- 'tileheight': $tile_width,
- 'latitude': $params{latitude},
- 'longitude': $params{longitude},
- 'pins': [ $pins_js ],
- 'tile_type': '$tile_type',
- 'maxResolution': $sf
-};
-</script>
-<div id="map_box">
- $params{pre}
- <div id="map"></div>
- <p id="copyright">$copyright</p>
- $params{post}
-</div>
-<div id="side">
-EOF
- return $out;
+sub watermark {
+ return 0;
}
1;
diff --git a/perllib/FixMyStreet/Map/Tilma/OpenLayers.pm b/perllib/FixMyStreet/Map/Tilma/OpenLayers.pm
new file mode 100644
index 000000000..31e9eb096
--- /dev/null
+++ b/perllib/FixMyStreet/Map/Tilma/OpenLayers.pm
@@ -0,0 +1,35 @@
+#!/usr/bin/perl
+#
+# FixMyStreet:Map::Tilma::1_10k_OL
+# Using tilma.mysociety.org with OpenLayers
+#
+# Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved.
+# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/
+
+package FixMyStreet::Map::Tilma::OpenLayers;
+
+use strict;
+
+sub TILE_WIDTH() { return $FixMyStreet::Map::map_class->tile_width; }
+sub SCALE_FACTOR() { return $FixMyStreet::Map::map_class->scale_factor; }
+sub TILE_TYPE() { return $FixMyStreet::Map::map_class->tile_type; }
+
+# display_map C PARAMS
+# PARAMS include:
+# latitude, longitude for the centre point of the map
+# TYPE is 1 if the map is clickable, 0 otherwise.
+# PINS is array of pins to show, location and colour
+sub display_map {
+ my ($self, $c, %params) = @_;
+ $c->stash->{map} = {
+ %params,
+ type => 'tilma/openlayers',
+ tile_type => TILE_TYPE,
+ tilewidth => TILE_WIDTH,
+ watermark => $self->watermark(),
+ copyright => $self->copyright(),
+ maxResolution => SCALE_FACTOR / TILE_WIDTH,
+ };
+}
+
+1;
diff --git a/perllib/FixMyStreet/Map/Tilma/Original.pm b/perllib/FixMyStreet/Map/Tilma/Original.pm
index 0af6ed277..6fa854cb2 100644
--- a/perllib/FixMyStreet/Map/Tilma/Original.pm
+++ b/perllib/FixMyStreet/Map/Tilma/Original.pm
@@ -11,7 +11,6 @@ package FixMyStreet::Map::Tilma::Original;
use strict;
use LWP::Simple;
-use Cobrand;
use mySociety::GeoUtil;
use mySociety::Locale;
use mySociety::Web qw(ent NewURL);
@@ -27,25 +26,15 @@ sub _ll_to_en {
return Utils::convert_latlon_to_en( $lat, $lon );
}
-sub header_js {
- return '
-<script type="text/javascript" src="/js/map-tilma.js"></script>
-';
-}
-
-# display_map Q PARAMS
+# display_map C PARAMS
# PARAMS include:
# latitude, longitude for the centre point of the map
-# TYPE is 1 if the map is clickable, 2 if clickable and has a form upload,
-# 0 if not clickable
+# CLICKABLE is set if the map is clickable
# PINS is array of pins to show, location and colour
-# PRE/POST are HTML to show above/below map
sub display_map {
- my ($self, $q, %params) = @_;
- $params{pre} ||= '';
- $params{post} ||= '';
+ my ($self, $c, %params) = @_;
my $mid_point = TILE_WIDTH; # Map is 2 TILE_WIDTHs in size, square.
- if (my $mp = Cobrand::tilma_mid_point(Page::get_cobrand($q))) {
+ if (my $mp = $c->cobrand->tilma_mid_point) {
$mid_point = $mp;
}
@@ -53,27 +42,18 @@ sub display_map {
( $params{easting}, $params{northing} ) =
_ll_to_en( $params{latitude}, $params{longitude} );
- # FIXME - convert all pins to lat, lng
- # all the pins are currently [lat, lng, colour] - convert them
- foreach my $pin ( @{ $params{pins} ||= [] } ) {
- my ( $lat, $lon ) = ( $pin->[0], $pin->[1] );
- my ( $e, $n ) = _ll_to_en( $lat, $lon );
- ( $pin->[0], $pin->[1] ) = ( $e, $n );
- }
-
# X/Y tile co-ords may be overridden in the query string
my @vars = qw(x y);
- my %input = map { $_ => $q->param($_) || '' } @vars;
+ my %input = map { $_ => $c->req->params->{$_} || '' } @vars;
($input{x}) = $input{x} =~ /^(\d+)/; $input{x} ||= 0;
($input{y}) = $input{y} =~ /^(\d+)/; $input{y} ||= 0;
- my ($x, $y, $px, $py) = os_to_px_with_adjust($q, $params{easting}, $params{northing}, $input{x}, $input{y});
+ my ($x, $y, $px, $py) = os_to_px_with_adjust($c, $params{easting}, $params{northing}, $input{x}, $input{y});
- my $pins = '';
foreach my $pin (@{$params{pins}}) {
- my $pin_x = os_to_px($pin->[0], $x);
- my $pin_y = os_to_px($pin->[1], $y, 1);
- $pins .= display_pin($q, $pin_x, $pin_y, $pin->[2]);
+ my ( $e, $n ) = _ll_to_en( $pin->{latitude}, $pin->{longitude} );
+ $pin->{px} = os_to_px($e, $x);
+ $pin->{py} = os_to_px($n, $y, 1);
}
$px = defined($px) ? $mid_point - $px : 0;
@@ -83,111 +63,66 @@ sub display_map {
my $url = 'http://tilma.mysociety.org/tileserver/' . TILE_TYPE . '/';
my $tiles_url = $url . ($x-1) . '-' . $x . ',' . ($y-1) . '-' . $y . '/RABX';
my $tiles = LWP::Simple::get($tiles_url);
- return '<div id="map_box"> <div id="map"><div id="drag">' . _("Unable to fetch the map tiles from the tile server.") . '</div></div></div><div id="side">' if !$tiles;
my $tileids = RABX::unserialise($tiles);
- my $tl = ($x-1) . '.' . $y;
- my $tr = $x . '.' . $y;
- my $bl = ($x-1) . '.' . ($y-1);
- my $br = $x . '.' . ($y-1);
- return '<div id="side">' if (!$tileids->[0][0] || !$tileids->[0][1] || !$tileids->[1][0] || !$tileids->[1][1]);
- my $tl_src = $url . $tileids->[0][0];
- my $tr_src = $url . $tileids->[0][1];
- my $bl_src = $url . $tileids->[1][0];
- my $br_src = $url . $tileids->[1][1];
-
- my $cobrand = Page::get_cobrand($q);
- my $root_path_js = Cobrand::root_path_js($cobrand, $q);
- my $out = FixMyStreet::Map::header($q, $params{type});
- my $img_type;
- if ($params{type}) {
- $out .= <<EOF;
-<input type="hidden" name="x" id="formX" value="$x">
-<input type="hidden" name="y" id="formY" value="$y">
-<input type="hidden" name="latitude" value="$params{latitude}">
-<input type="hidden" name="longitude" value="$params{longitude}">
-EOF
- $img_type = '<input type="image"';
- } else {
- $img_type = '<img';
- }
- my $imgw = TILE_WIDTH . 'px';
- my $tile_width = TILE_WIDTH;
- my $tile_type = TILE_TYPE;
- $out .= <<EOF;
-<script type="text/javascript">
-$root_path_js
-var fixmystreet = {
- 'x': $x - 3,
- 'y': $y - 3,
- 'start_x': $px,
- 'start_y': $py,
- 'tile_type': '$tile_type',
- 'tilewidth': $tile_width,
- 'tileheight': $tile_width
-};
-</script>
-<div id="map_box">
-$params{pre}
- <div id="map"><div id="drag">
- $img_type alt="NW map tile" id="t2.2" name="tile_$tl" src="$tl_src" style="top:0px; left:0;">$img_type alt="NE map tile" id="t2.3" name="tile_$tr" src="$tr_src" style="top:0px; left:$imgw;"><br>$img_type alt="SW map tile" id="t3.2" name="tile_$bl" src="$bl_src" style="top:$imgw; left:0;">$img_type alt="SE map tile" id="t3.3" name="tile_$br" src="$br_src" style="top:$imgw; left:$imgw;">
- <div id="pins">$pins</div>
- </div>
-EOF
- $out .= '<div id="watermark"></div>' if $self->watermark();
- $out .= compass($q, $x, $y);
- my $copyright = $self->copyright();
- $out .= <<EOF;
- </div>
- <p id="copyright">$copyright</p>
-$params{post}
-</div>
-<div id="side">
-EOF
- return $out;
+ $c->stash->{map} = {
+ %params,
+ type => 'tilma/original',
+ tiles => $tiles,
+ url => $url,
+ tileids => $tileids,
+ x => $x,
+ y => $y,
+ px => $px,
+ py => $py,
+ tile_type => TILE_TYPE,
+ tilewidth => TILE_WIDTH,
+ watermark => $self->watermark(),
+ copyright => $self->copyright(),
+ };
}
sub display_pin {
- my ($q, $px, $py, $col, $num) = @_;
+ my ($c, $px, $py, $col, $id, $title, $num) = @_;
$num = '' if !$num || $num > 9;
- my $host = Page::base_url_with_lang($q, undef);
+ my $host = $c->cobrand->base_url_with_lang;
my %cols = (red=>'R', green=>'G', blue=>'B', purple=>'P');
my $out = '<img class="pin" src="' . $host . '/i/pin' . $cols{$col}
. $num . '.gif" alt="' . _('Problem') . '" style="top:' . ($py-59)
. 'px; left:' . ($px) . 'px; position: absolute;">';
- return $out unless $_ && $_->{id} && $col ne 'blue';
- my $cobrand = Page::get_cobrand($q);
- my $url = Cobrand::url($cobrand, NewURL($q, -url => '/report/' . $_->{id}), $q);
- $out = '<a title="' . ent($_->{title}) . '" href="' . $url . '">' . $out . '</a>';
+ return $out unless $id;
+ my $url = $c->uri_for( '/report/' . $id );
+ $out = '<a title="' . ent($title) . '" href="' . $url . '">' . $out . '</a>';
return $out;
}
sub map_pins {
- my ($self, $q, $x, $y, $sx, $sy, $interval) = @_;
+ my ($self, $c, $x, $y, $sx, $sy, $interval) = @_;
my $e = tile_to_os($x);
my $n = tile_to_os($y);
my ( $lat, $lon ) = Utils::convert_en_to_latlon( $e, $n );
my ( $around_map, $around_map_list, $nearby, $dist ) =
- FixMyStreet::Map::map_features( $q, $lat, $lon, $interval );
+ FixMyStreet::Map::map_features( $c, $lat, $lon, $interval );
my $pins = '';
foreach (@$around_map) {
- ( $_->{easting}, $_->{northing} ) =
- _ll_to_en( $_->{latitude}, $_->{longitude} );
- my $px = os_to_px($_->{easting}, $sx);
- my $py = os_to_px($_->{northing}, $sy, 1);
- my $col = $_->{state} eq 'fixed' ? 'green' : 'red';
- $pins .= display_pin($q, $px, $py, $col);
+ my ( $easting, $northing ) =
+ _ll_to_en( $_->latitude, $_->longitude );
+ my $px = os_to_px($easting, $sx);
+ my $py = os_to_px($northing, $sy, 1);
+ my $col = $_->state eq 'fixed' ? 'green' : 'red';
+ $pins .= display_pin($c, $px, $py, $col, $_->id, $_->title);
}
foreach (@$nearby) {
- ( $_->{easting}, $_->{northing} ) =
- _ll_to_en( $_->{latitude}, $_->{longitude} );
- my $px = os_to_px($_->{easting}, $sx);
- my $py = os_to_px($_->{northing}, $sy, 1);
- my $col = $_->{state} eq 'fixed' ? 'green' : 'red';
- $pins .= display_pin($q, $px, $py, $col);
+ my $p = $_->problem;
+ my ( $easting, $northing ) =
+ _ll_to_en( $p->latitude, $p->longitude );
+ my $px = os_to_px($easting, $sx);
+ my $py = os_to_px($northing, $sy, 1);
+ my $col = $p->state eq 'fixed' ? 'green' : 'red';
+ $pins .= display_pin($c, $px, $py, $col, $p->id, $p->title);
}
return ($pins, $around_map_list, $nearby, $dist);
@@ -262,7 +197,7 @@ sub click_to_os {
# tile they were), convert to WGS84 and return.
sub click_to_wgs84 {
my $self = shift;
- my $q = shift;
+ my $c = shift;
my ( $easting, $northing ) = click_to_os(@_);
my ( $lat, $lon ) = mySociety::GeoUtil::national_grid_to_wgs84( $easting, $northing, 'G' );
return ( $lat, $lon );
@@ -272,7 +207,7 @@ sub click_to_wgs84 {
# of the map (either to get the point near the middle, or the override X,Y),
# and the pixel co-ords of the point, relative to that map.
sub os_to_px_with_adjust {
- my ($q, $easting, $northing, $in_x, $in_y) = @_;
+ my ($c, $easting, $northing, $in_x, $in_y) = @_;
my $x = os_to_tile($easting);
my $y = os_to_tile($northing);
@@ -289,7 +224,7 @@ sub os_to_px_with_adjust {
my $px = os_to_px($easting, $x_tile);
my $py = os_to_px($northing, $y_tile, 1);
- if ($q->{site} eq 'barnet') { # Map is 380px, so might need to adjust
+ if ($c->cobrand->tilma_mid_point == 189) { # Map is 380px, so might need to adjust
if (!$in_x && $px > 380) {
$x_tile++;
$px = os_to_px($easting, $x_tile);
@@ -303,35 +238,4 @@ sub os_to_px_with_adjust {
return ($x_tile, $y_tile, $px, $py);
}
-sub compass ($$$) {
- my ( $q, $x, $y ) = @_;
- my @compass;
- for ( my $i = $x - 1 ; $i <= $x + 1 ; $i++ ) {
- for ( my $j = $y - 1 ; $j <= $y + 1 ; $j++ ) {
- $compass[$i][$j] = NewURL( $q, x => $i, y => $j );
- }
- }
- my $recentre = NewURL($q);
- my $host = Page::base_url_with_lang( $q, undef );
- return <<EOF;
-<table cellpadding="0" cellspacing="0" border="0" id="compass">
-<tr valign="bottom">
-<td align="right"><a rel="nofollow" href="${compass[$x-1][$y+1]}"><img src="$host/i/arrow-northwest.gif" alt="NW" width=11 height=11></a></td>
-<td align="center"><a rel="nofollow" href="${compass[$x][$y+1]}"><img src="$host/i/arrow-north.gif" vspace="3" alt="N" width=13 height=11></a></td>
-<td><a rel="nofollow" href="${compass[$x+1][$y+1]}"><img src="$host/i/arrow-northeast.gif" alt="NE" width=11 height=11></a></td>
-</tr>
-<tr>
-<td><a rel="nofollow" href="${compass[$x-1][$y]}"><img src="$host/i/arrow-west.gif" hspace="3" alt="W" width=11 height=13></a></td>
-<td align="center"><a rel="nofollow" href="$recentre"><img src="$host/i/rose.gif" alt="Recentre" width=35 height=34></a></td>
-<td><a rel="nofollow" href="${compass[$x+1][$y]}"><img src="$host/i/arrow-east.gif" hspace="3" alt="E" width=11 height=13></a></td>
-</tr>
-<tr valign="top">
-<td align="right"><a rel="nofollow" href="${compass[$x-1][$y-1]}"><img src="$host/i/arrow-southwest.gif" alt="SW" width=11 height=11></a></td>
-<td align="center"><a rel="nofollow" href="${compass[$x][$y-1]}"><img src="$host/i/arrow-south.gif" vspace="3" alt="S" width=13 height=11></a></td>
-<td><a rel="nofollow" href="${compass[$x+1][$y-1]}"><img src="$host/i/arrow-southeast.gif" alt="SE" width=11 height=11></a></td>
-</tr>
-</table>
-EOF
-}
-
1;
diff --git a/perllib/FixMyStreet/Roles/Abuser.pm b/perllib/FixMyStreet/Roles/Abuser.pm
new file mode 100644
index 000000000..b9e951305
--- /dev/null
+++ b/perllib/FixMyStreet/Roles/Abuser.pm
@@ -0,0 +1,29 @@
+package FixMyStreet::Roles::Abuser;
+
+use Moose::Role;
+
+=head2 is_from_abuser
+
+ $bool = $alert->is_from_abuser( );
+
+Returns true if the user's email or its domain is listed in the 'abuse' table.
+
+=cut
+
+sub is_from_abuser {
+ my $self = shift;
+
+ # get the domain
+ my $email = $self->user->email;
+ my ($domain) = $email =~ m{ @ (.*) \z }x;
+
+ # search for an entry in the abuse table
+ my $abuse_rs = $self->result_source->schema->resultset('Abuse');
+
+ return
+ $abuse_rs->find( { email => $email } )
+ || $abuse_rs->find( { email => $domain } )
+ || undef;
+}
+
+1;
diff --git a/perllib/FixMyStreet/TestMech.pm b/perllib/FixMyStreet/TestMech.pm
new file mode 100644
index 000000000..3dd4e84e9
--- /dev/null
+++ b/perllib/FixMyStreet/TestMech.pm
@@ -0,0 +1,494 @@
+package FixMyStreet::TestMech;
+use base qw(Test::WWW::Mechanize::Catalyst Test::Builder::Module);
+
+use strict;
+use warnings;
+
+BEGIN {
+ use FixMyStreet;
+ FixMyStreet->test_mode(1);
+}
+
+use Test::WWW::Mechanize::Catalyst 'FixMyStreet::App';
+use Test::More;
+use Web::Scraper;
+use Carp;
+use Email::Send::Test;
+use Digest::SHA1 'sha1_hex';
+use JSON;
+
+=head1 NAME
+
+FixMyStreet::TestMech - T::WWW::M:C but with FMS specific smarts
+
+=head1 DESCRIPTION
+
+This module subclasses L<Test::WWW::Mechanize::Catalyst> and adds some
+FixMyStreet specific smarts - such as the ability to scrape the resulting page
+for form error messages.
+
+Note - using this module puts L<FixMyStreet::App> into test mode - so for
+example emails will not get sent.
+
+=head1 METHODS
+
+=head2 check_not_logged_in, check_logged_in
+
+ $bool = $mech->check_not_logged_in();
+ $bool = $mech->check_logged_in();
+
+Check that the current mech is not logged or logged in as a user. Produces test output.
+Returns true test passed, false otherwise.
+
+=cut
+
+sub not_logged_in_ok {
+ my $mech = shift;
+ $mech->builder->ok( $mech->get('/auth/check_auth')->code == 401,
+ "not logged in" );
+}
+
+sub logged_in_ok {
+ my $mech = shift;
+ $mech->builder->ok( $mech->get('/auth/check_auth')->code == 200,
+ "logged in" );
+}
+
+=head2 create_user_ok
+
+ $user = $mech->create_user_ok( $email );
+
+Create a test user (or find it and return if it already exists).
+
+=cut
+
+sub create_user_ok {
+ my $self = shift;
+ my ($email) = @_;
+
+ my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => $email } );
+ ok $user, "found/created user for $email";
+
+ return $user;
+}
+
+=head2 log_in_ok
+
+ $user = $mech->log_in_ok( $email_address );
+
+Log in with the email given. If email does not match an account then create one.
+
+=cut
+
+sub log_in_ok {
+ my $mech = shift;
+ my $email = shift;
+
+ my $user = $mech->create_user_ok($email);
+
+ # store the old password and then change it
+ my $old_password_sha1 = $user->password;
+ $user->update( { password => sha1_hex('secret') } );
+
+ # log in
+ $mech->get_ok('/auth');
+ $mech->submit_form_ok(
+ { with_fields => { email => $email, password => 'secret' } },
+ "login using form" );
+ $mech->logged_in_ok;
+
+ # restore the password (if there was one)
+ $user->update( { password => $old_password_sha1 } ) if $old_password_sha1;
+
+ return $user;
+}
+
+=head2 log_out_ok
+
+ $bool = $mech->log_out_ok( );
+
+Log out the current user
+
+=cut
+
+sub log_out_ok {
+ my $mech = shift;
+ $mech->get_ok('/auth/logout');
+ $mech->not_logged_in_ok;
+}
+
+=head2 delete_user
+
+ $mech->delete_user( $user );
+ $mech->delete_user( $email );
+
+Delete the current user, including linked objects like problems etc. Can be
+either a user object or an email address.
+
+=cut
+
+sub delete_user {
+ my $mech = shift;
+ my $email_or_user = shift;
+
+ my $user =
+ ref $email_or_user
+ ? $email_or_user
+ : FixMyStreet::App->model('DB::User')
+ ->find( { email => $email_or_user } );
+
+ # If no user found we can't delete them
+ if ( !$user ) {
+ ok( 1, "No user found to delete" );
+ return 1;
+ }
+
+ $mech->log_out_ok;
+ for my $p ( $user->problems ) {
+ ok( $_->delete, "delete comment " . $_->text ) for $p->comments;
+ ok( $_->delete, "delete questionnaire " . $_->id ) for $p->questionnaires;
+ ok( $p->delete, "delete problem " . $p->title );
+ }
+ for my $a ( $user->alerts ) {
+ $a->alert_sents->delete;
+ ok( $a->delete, "delete alert " . $a->alert_type );
+ }
+ ok( $_->delete, "delete comment " . $_->text ) for $user->comments;
+ ok $user->delete, "delete test user " . $user->email;
+
+ return 1;
+}
+
+=head2 clear_emails_ok
+
+ $bool = $mech->clear_emails_ok();
+
+Clear the email queue.
+
+=cut
+
+sub clear_emails_ok {
+ my $mech = shift;
+ Email::Send::Test->clear;
+ $mech->builder->ok( 1, 'cleared email queue' );
+ return 1;
+}
+
+=head2 email_count_is
+
+ $bool = $mech->email_count_is( $number );
+
+Check that the number of emails in queue is correct.
+
+=cut
+
+sub email_count_is {
+ my $mech = shift;
+ my $number = shift || 0;
+
+ $mech->builder->is_num( scalar( Email::Send::Test->emails ),
+ $number, "checking for $number email(s) in the queue" );
+}
+
+=head2 get_email
+
+ $email = $mech->get_email;
+
+In scalar context returns first email in queue and fails a test if there are not exactly one emails in the queue.
+
+In list context returns all the emails (or none).
+
+=cut
+
+sub get_email {
+ my $mech = shift;
+ my @emails = Email::Send::Test->emails;
+
+ return @emails if wantarray;
+
+ $mech->email_count_is(1) || return undef;
+ return $emails[0];
+}
+
+=head2 form_errors
+
+ my $arrayref = $mech->form_errors;
+
+Find all the form errors on the current page and return them in page order as an
+arrayref of TEXTs. If none found return empty arrayref.
+
+=cut
+
+sub form_errors {
+ my $mech = shift;
+ my $result = scraper {
+ process 'div.form-error', 'errors[]', 'TEXT';
+ }
+ ->scrape( $mech->response );
+ return $result->{errors} || [];
+}
+
+=head2 page_errors
+
+ my $arrayref = $mech->page_errors;
+
+Find all the form errors on the current page and return them in page order as an
+arrayref of TEXTs. If none found return empty arrayref.
+
+=cut
+
+sub page_errors {
+ my $mech = shift;
+ my $result = scraper {
+ process 'p.error', 'errors[]', 'TEXT';
+ process 'ul.error li', 'errors[]', 'TEXT';
+ }
+ ->scrape( $mech->response );
+ return $result->{errors} || [];
+}
+
+=head2 import_errors
+
+ my $arrayref = $mech->import_errors;
+
+Takes the text output from the import post result and returns all the errors as
+an arrayref.
+
+=cut
+
+sub import_errors {
+ my $mech = shift;
+ my @errors = #
+ grep { $_ } #
+ map { s{^ERROR:\s*(.*)$}{$1}g ? $_ : undef; } #
+ split m/\n+/, $mech->response->content;
+ return \@errors;
+}
+
+=head2 pc_alternatives
+
+ my $arrayref = $mech->pc_alternatives;
+
+Find all the suggestions for near matches for a location. Return text presented to user as arrayref, empty arrayref if none found.
+
+=cut
+
+sub pc_alternatives {
+ my $mech = shift;
+ my $result = scraper {
+ process 'ul.pc_alternatives li', 'pc_alternatives[]', 'TEXT';
+ }
+ ->scrape( $mech->response );
+ return $result->{pc_alternatives} || [];
+}
+
+=head2 extract_location
+
+ $hashref = $mech->extract_location( );
+
+Extracts the location from the current page. Looks for inputs with the names
+C<pc>, C<latitude> and C<longitude> and returns their values in a hashref with
+those keys. If no values found then the values in hashrof are C<undef>.
+
+=cut
+
+sub extract_location {
+ my $mech = shift;
+
+ my $result = scraper {
+ process 'input[name="pc"]', pc => '@value';
+ process 'input[name="latitude"]', latitude => '@value';
+ process 'input[name="longitude"]', longitude => '@value';
+ }
+ ->scrape( $mech->response );
+
+ return {
+ pc => undef,
+ latitude => undef,
+ longitude => undef,
+ %$result
+ };
+}
+
+=head2 extract_problem_meta
+
+ $meta = $mech->extract_problem_meta;
+
+Returns the problem meta information ( submitted by, at etc ) from a
+problem report page
+
+=cut
+
+sub extract_problem_meta {
+ my $mech = shift;
+
+ my $result = scraper {
+ process 'div#side p em', 'meta', 'TEXT';
+ }
+ ->scrape( $mech->response );
+
+ my ($meta) = map { s/^\s+//; s/\s+$//; $_; } ($result->{meta});
+
+ return $meta;
+}
+
+=head2 extract_problem_title
+
+ $title = $mech->extract_problem_title;
+
+Returns the problem title from a problem report page.
+
+=cut
+
+sub extract_problem_title {
+ my $mech = shift;
+
+ my $result = scraper {
+ process 'div#side h1', 'title', 'TEXT';
+ }
+ ->scrape( $mech->response );
+
+ return $result->{title};
+}
+
+=head2 extract_problem_banner
+
+ $banner = $mech->extract_problem_banner;
+
+Returns the problem title from a problem report page. Returns a hashref with id and text.
+
+=cut
+
+sub extract_problem_banner {
+ my $mech = shift;
+
+ my $result = scraper {
+ process 'div#side > p', id => '@id', text => 'TEXT';
+ }
+ ->scrape( $mech->response );
+
+ return $result;
+}
+
+=head2 extract_update_metas
+
+ $metas = $mech->extract_update_metas;
+
+Returns an array ref of all the update meta information on the page. Strips whitespace from
+the start and end of all of them.
+
+=cut
+
+sub extract_update_metas {
+ my $mech = shift;
+
+ my $result = scraper {
+ process 'div#updates div.problem-update p em', 'meta[]', 'TEXT';
+ }
+ ->scrape( $mech->response );
+
+ my @metas = map { s/^\s+//; s/\s+$//; $_; } @{ $result->{meta} };
+
+ return \@metas;
+}
+
+=head2 visible_form_values
+
+ $hashref = $mech->visible_form_values( );
+
+Return all the visible form values on the page - ie not the hidden ones.
+
+=cut
+
+sub visible_form_values {
+ my $mech = shift;
+ my $name = shift || '';
+
+ my $form;
+
+ if ($name) {
+ for ( $mech->forms ) {
+ $form = $_ if ( $_->attr('name') || '' ) eq $name;
+ }
+ croak "Can't find form named $name - can't continue..."
+ unless $form;
+ }
+ else {
+ my @forms =
+ grep { ( $_->attr('name') || '' ) ne 'overrides_form' } # ignore overrides
+ $mech->forms;
+
+ croak "Found no forms - can't continue..."
+ unless @forms;
+
+ croak "Found several forms - don't know which to use..."
+ if @forms > 1;
+
+ $form = $forms[0];
+ }
+
+ my @visible_fields =
+ grep { ref($_) ne 'HTML::Form::SubmitInput' }
+ grep { ref($_) ne 'HTML::Form::ImageInput' }
+ grep { ref($_) ne 'HTML::Form::TextInput' || $_->type ne 'hidden' }
+ $form->inputs;
+
+ my @visible_field_names = map { $_->name } @visible_fields;
+
+ my %params = map { $_ => $form->value($_) } @visible_field_names;
+
+ return \%params;
+}
+
+=head2 session_cookie_expiry
+
+ $expiry = $mech->session_cookie_expiry( );
+
+Returns the current expiry time for the session cookie. Might be '0' which
+indicates it expires at end of browser session.
+
+=cut
+
+sub session_cookie_expiry {
+ my $mech = shift;
+
+ my $cookie_name = 'fixmystreet_app_session';
+ my $expires = 'not found';
+
+ $mech #
+ ->cookie_jar #
+ ->scan( sub { $expires = $_[8] if $_[1] eq $cookie_name } );
+
+ croak "Could not find cookie '$cookie_name'"
+ if $expires && $expires eq 'not found';
+
+ return $expires || 0;
+}
+
+=head2 get_ok_json
+
+ $decoded = $mech->get_ok_json( $url );
+
+Get the url, check that it was JSON and then decode and return the body.
+
+=cut
+
+sub get_ok_json {
+ my $mech = shift;
+ my $url = shift;
+
+ # try to get the response
+ $mech->get_ok($url)
+ || return undef;
+ my $res = $mech->response;
+
+ # check that the content-type of response is correct
+ croak "Response was not JSON"
+ unless $res->header('Content-Type') =~ m{^application/json\b};
+
+ return decode_json( $res->content );
+}
+
+1;
diff --git a/perllib/Page.pm b/perllib/Page.pm
deleted file mode 100644
index a52f455b4..000000000
--- a/perllib/Page.pm
+++ /dev/null
@@ -1,781 +0,0 @@
-#!/usr/bin/perl
-#
-# Page.pm:
-# Various HTML stuff for the FixMyStreet site.
-#
-# Copyright (c) 2006 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/
-#
-# $Id: Page.pm,v 1.230 2010-01-15 17:08:55 matthew Exp $
-#
-
-package Page;
-
-use strict;
-use Carp;
-use mySociety::CGIFast qw(-no_xhtml);
-use Data::Dumper;
-use Encode;
-use Error qw(:try);
-use File::Slurp;
-use HTTP::Date; # time2str
-use Image::Magick;
-use Image::Size;
-use IO::String;
-use POSIX qw(strftime);
-use URI::Escape;
-use Text::Template;
-
-use Memcached;
-use Problems;
-use Cobrand;
-
-use mySociety::Config;
-use mySociety::DBHandle qw/dbh select_all/;
-use mySociety::Email;
-use mySociety::EvEl;
-use mySociety::Locale;
-use mySociety::MaPit;
-use mySociety::TempFiles;
-use mySociety::WatchUpdate;
-use mySociety::Web qw(ent);
-
-BEGIN {
- (my $dir = __FILE__) =~ s{/[^/]*?$}{};
- mySociety::Config::set_file("$dir/../conf/general");
-}
-
-# Under the BEGIN so that the config has been set.
-use FixMyStreet::Map;
-
-my $lastmodified;
-
-sub do_fastcgi {
- my ($func, $lm, $binary) = @_;
-
- try {
- my $W = new mySociety::WatchUpdate();
- while (my $q = new mySociety::Web(unicode => 1)) {
- next if $lm && $q->Maybe304($lm);
- $lastmodified = $lm;
- microsite($q);
- my $str_fh = IO::String->new;
- my $old_fh = select($str_fh);
- &$func($q);
- select($old_fh) if defined $old_fh;
- print $binary ? ${$str_fh->string_ref} : encode_utf8(${$str_fh->string_ref});
- dbh()->rollback() if $mySociety::DBHandle::conf_ok;
- $W->exit_if_changed();
- }
- } catch Error::Simple with {
- report_error(@_);
- } catch Error with {
- report_error(@_);
- };
- dbh()->rollback() if $mySociety::DBHandle::conf_ok;
- exit(0);
-}
-
-sub report_error {
- my $E = shift;
- my $msg = sprintf('%s:%d: %s', $E->file(), $E->line(), CGI::escapeHTML($E->text()));
- warn "caught fatal exception: $msg";
- warn "aborting";
- ent($msg);
- my $contact_email = mySociety::Config::get('CONTACT_EMAIL');
- my $trylater = sprintf(_('Please try again later, or <a href="mailto:%s">email us</a> to let us know.'), $contact_email);
- my $somethingwrong = _("Sorry! Something's gone wrong.");
- my $errortext = _("The text of the error was:");
-
- my $msg_br = join '<br><br>', split m{\n}, $msg;
-
- print "Status: 500\nContent-Type: text/html; charset=utf-8\n\n",
- qq(<html><head><title>$somethingwrong</title></head></html>),
- q(<body>),
- qq(<h1>$somethingwrong</h1>),
- qq(<p>$trylater</p>),
- q(<hr>),
- qq(<p>$errortext</p>),
- qq(<blockquote class="errortext">$msg_br</blockquote>),
- q(</body></html>);
-}
-
-=item microsite Q
-
-Work out what site we're on, template appropriately
-
-=cut
-sub microsite {
- my $q = shift;
- my $host = $ENV{HTTP_HOST} || '';
- $q->{site} = 'fixmystreet';
- my $allowed_cobrands = Cobrand::get_allowed_cobrands();
- foreach my $cobrand (@{$allowed_cobrands}){
- $q->{site} = $cobrand if $host =~ /$cobrand/;
- }
-
- my $lang;
- $lang = 'cy' if $host =~ /cy/;
- $lang = 'en-gb' if $host =~ /^en\./;
- Cobrand::set_lang_and_domain(get_cobrand($q), $lang, 1);
-
- FixMyStreet::Map::set_map_class($q->param('map'));
-
- Problems::set_site_restriction($q);
- Memcached::set_namespace(mySociety::Config::get('BCI_DB_NAME') . ":");
-}
-=item get_cobrand Q
-
-Return the cobrand for a query
-
-=cut
-sub get_cobrand {
- my $q = shift;
- my $cobrand = '';
- $cobrand = $q->{site} if $q->{site} ne 'fixmystreet';
- return $cobrand;
-}
-
-=item base_url_with_lang Q REVERSE EMAIL
-
-Return the base URL for the site. Reverse the language component if REVERSE is set to one. If EMAIL is set to
-one, return the base URL to use in emails.
-
-=cut
-
-sub base_url_with_lang {
- my ($q, $reverse, $email) = @_;
- my $base;
- my $cobrand = get_cobrand($q);
- if ($email) {
- $base = Cobrand::base_url_for_emails($cobrand, Cobrand::extra_data($cobrand, $q));
- } else {
- $base = Cobrand::base_url($cobrand);
- }
- return $base unless $q->{site} eq 'emptyhomes';
- my $lang = $mySociety::Locale::lang;
- if ($reverse && $lang eq 'en-gb') {
- $base =~ s{http://}{$&cy.};
- } elsif ($reverse) {
- $base =~ s{http://}{$&en.};
- } elsif ($lang eq 'cy') {
- $base =~ s{http://}{$&cy.};
- } else {
- $base =~ s{http://}{$&en.};
- }
- return $base;
-}
-
-=item template_root
-
-Returns the path from which template files will be read.
-
-=cut
-
-sub template_root($;$) {
- my ($q, $fallback) = @_;
- return '/../templates/website/' if $q->{site} eq 'fixmystreet' || $fallback;
- return '/../templates/website/cobrands/' . $q->{site} . '/';
-}
-
-=item template_vars QUERY PARAMS
-
-Return a hash of variables that can be substituted into header and footer templates.
-QUERY is the incoming request
-PARAMS contains a few things used to generate variables, such as lang, title, and rss.
-
-=cut
-
-sub template_vars ($%) {
- my ($q, %params) = @_;
- my %vars;
- my $host = base_url_with_lang($q, undef);
- my $lang_url = base_url_with_lang($q, 1);
- $lang_url .= $ENV{REQUEST_URI} if $ENV{REQUEST_URI};
-
- my $site_title = Cobrand::site_title(get_cobrand($q));
- $site_title = _('FixMyStreet') unless $site_title;
-
- %vars = (
- 'report' => _('Report a problem'),
- 'reports' => _('All reports'),
- 'alert' => _('Local alerts'),
- 'faq' => _('Help'),
- 'about' => _('About us'),
- 'site_title' => $site_title,
- 'host' => $host,
- 'lang_code' => $params{lang},
- 'lang' => $params{lang} eq 'en-gb' ? 'Cymraeg' : 'English',
- 'lang_url' => $lang_url,
- 'title' => $params{title},
- 'rss' => '',
- map_js => $params{js} || '',
- );
-
- if ($params{rss}) {
- $vars{rss} = '<link rel="alternate" type="application/rss+xml" title="' . $params{rss}[0] . '" href="' . $params{rss}[1] . '">';
- }
-
- if ($params{robots}) {
- $vars{robots} = '<meta name="robots" content="' . $params{robots} . '">';
- }
-
- my $home = !$params{title} && $ENV{SCRIPT_NAME} eq '/index.cgi' && !$ENV{QUERY_STRING};
- $vars{heading_element_start} = $home ? '<h1 id="header">' : '<div id="header"><a href="/">';
- $vars{heading} = _('Fix<span id="my">My</span>Street');
- $vars{heading_element_end} = $home ? '</h1>' : '</a></div>';
-
- return \%vars;
-}
-
-=item template Q [PARAM VALUE ...]
-
-Return the correct template given PARAMs
-
-=cut
-sub template($%){
- my ($q, %params) = @_;
- my $template;
- if ($params{template}){
- $template = $params{template};
- }else{
- $template = $q->{site};
- }
- return $template;
-}
-
-=item template_include
-
-Return HTML for a template, given a template name, request,
-template root, and any parameters needed.
-
-=cut
-
-sub template_include {
- my ($template, $q, $template_root, %params) = @_;
- (my $file = __FILE__) =~ s{/[^/]*?$}{};
- my $template_file = $file . $template_root . $template;
- $template_file = $file . template_root($q, 1) . $template unless -e $template_file;
- return undef unless -e $template_file;
-
- $template = Text::Template->new(
- TYPE => 'STRING',
- # Don't use FILE, because we need to make sure it's Unicode characters
- SOURCE => decode_utf8(File::Slurp::read_file($template_file)),
- DELIMITERS => ['{{', '}}'],
- );
- return $template->fill_in(HASH => \%params);
-}
-
-=item header Q [PARAM VALUE ...]
-
-Return HTML for the top of the page, given PARAMs (TITLE is required).
-
-=cut
-sub header ($%) {
- my ($q, %params) = @_;
- my $context = $params{context};
- my $default_params = Cobrand::header_params(get_cobrand($q), $q, %params);
- my %default_params = %{$default_params};
- %params = (%default_params, %params);
- my %permitted_params = map { $_ => 1 } qw(title rss expires lastmodified template cachecontrol context status_code robots js);
- foreach (keys %params) {
- croak "bad parameter '$_'" if (!exists($permitted_params{$_}));
- }
-
- my %head = ();
- $head{-expires} = $params{expires} if $params{expires};
- $head{'-last-modified'} = time2str($params{lastmodified}) if $params{lastmodified};
- $head{'-last-modified'} = time2str($lastmodified) if $lastmodified;
- $head{'-Cache-Control'} = $params{cachecontrol} if $params{cachecontrol};
- $head{'-status'} = $params{status_code} if $params{status_code};
- print $q->header(%head);
-
- $params{title} ||= '';
- $params{title} .= ' - ' if $params{title};
- $params{title} = ent($params{title});
- $params{lang} = $mySociety::Locale::lang;
-
- my $vars = template_vars($q, %params);
- my $html = template_include('header', $q, template_root($q), %$vars);
- my $cache_val = $default_params{cachecontrol};
- if (mySociety::Config::get('STAGING_SITE')) {
- $html .= '<p class="error">' . _("This is a developer site; things might break at any time, and the database will be periodically deleted.") . '</p>';
- }
- return $html;
-}
-
-=item footer
-
-=cut
-sub footer {
- my ($q, %params) = @_;
-
- my $pc = $q->param('pc') || '';
- $pc = '?pc=' . URI::Escape::uri_escape_utf8($pc) if $pc;
-
- my $creditline = _('Built by <a href="http://www.mysociety.org/">mySociety</a>, using some <a href="http://github.com/mysociety/fixmystreet">clever</a>&nbsp;<a href="https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa">code</a>.');
- if (mySociety::Config::get('COUNTRY') eq 'NO') {
- $creditline = _('Built by <a href="http://www.mysociety.org/">mySociety</a> and maintained by <a href="http://www.nuug.no/">NUUG</a>, using some <a href="http://github.com/mysociety/fixmystreet">clever</a>&nbsp;<a href="https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa">code</a>.');
- }
-
- %params = (%params,
- navigation => _('Navigation'),
- report => _("Report a problem"),
- reports => _("All reports"),
- alerts => _("Local alerts"),
- help => _("Help"),
- contact => _("Contact"),
- pc => $pc,
- orglogo => _('<a href="http://www.mysociety.org/"><img id="logo" width="133" height="26" src="/i/mysociety-dark.png" alt="View mySociety.org"><span id="logoie"></span></a>'),
- creditline => $creditline,
- );
-
- my $html = template_include('footer', $q, template_root($q), %params);
- if ($html) {
- my $lang = $mySociety::Locale::lang;
- if ($q->{site} eq 'emptyhomes' && $lang eq 'cy') {
- $html =~ s/25 Walter Road<br>Swansea/25 Heol Walter<br>Abertawe/;
- }
- return $html;
- }
-
- my $piwik = "";
- if (mySociety::Config::get('BASE_URL') eq "http://www.fixmystreet.com") {
- $piwik = <<EOF;
-<!-- Piwik -->
-<script type="text/javascript">
-var pkBaseURL = (("https:" == document.location.protocol) ? "https://piwik.mysociety.org/" : "http://piwik.mysociety.org/");
-document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
-</script><script type="text/javascript">
-try {
-var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", 8);
-piwikTracker.trackPageView();
-piwikTracker.enableLinkTracking();
-} catch( err ) {}
-</script><noscript><p><img src="http://piwik.mysociety.org/piwik.php?idsite=8" style="border:0" alt=""/></p></noscript>
-<!-- End Piwik Tag -->
-EOF
- }
-
- return <<EOF;
-</div></div>
-<h2 class="v">$params{navigation}</h2>
-<ul id="navigation">
-<li><a href="/">$params{report}</a></li>
-<li><a href="/reports">$params{reports}</a></li>
-<li><a href="/alert$params{pc}">$params{alerts}</a></li>
-<li><a href="/faq">$params{help}</a></li>
-<li><a href="/contact">$params{contact}</a></li>
-</ul>
-
-$params{orglogo}
-
-<p id="footer">$params{creditline}</p>
-
-$piwik
-
-</body>
-</html>
-EOF
-}
-
-=item error_page Q MESSAGE
-
-=cut
-sub error_page ($$) {
- my ($q, $message);
- my $html = header($q, title=>_("Error"))
- . $q->p($message)
- . footer($q);
- print $q->header(-content_length => length($html)), $html;
-}
-
-# send_email TO (NAME) TEMPLATE-NAME PARAMETERS
-# TEMPLATE-NAME is a full filename here.
-sub send_email {
- my ($q, $recipient_email_address, $name, $template, %h) = @_;
-
- $template = File::Slurp::read_file("$FindBin::Bin/../templates/emails/$template");
- my $to = $name ? [[$recipient_email_address, $name]] : $recipient_email_address;
- my $cobrand = get_cobrand($q);
- my $sender = Cobrand::contact_email($cobrand);
- my $sender_name = Cobrand::contact_name($cobrand);
- $sender =~ s/team/fms-DO-NOT-REPLY/;
-
- # Can send email either via EvEl (if configured) or via local MTA on
- # machine. If EvEl fails (server down etc) fall back to local sending
-
- my $email_building_args = {
- _template_ => _($template),
- _parameters_ => \%h,
- From => [ $sender, _($sender_name) ],
- To => $to,
- };
-
- my $email_sent_successfully = 0;
-
- if ( my $EvEl_url = mySociety::Config::get('EVEL_URL') ) {
- eval {
- mySociety::EvEl::send( $email_building_args, $recipient_email_address );
- $email_sent_successfully = 1;
- };
-
- warn "ERROR: sending email via '$EvEl_url' failed: $@" if $@;
- }
-
- # If not sent through EvEL, or EvEl failed
- if ( !$email_sent_successfully ) {
- my $email = mySociety::Locale::in_gb_locale {
- mySociety::Email::construct_email( $email_building_args );
- };
-
- my $send_email_result =
- mySociety::EmailUtil::send_email( $email, $sender, $recipient_email_address );
- $email_sent_successfully = !$send_email_result; # invert result
- }
-
- # Could not send email - die
- if ( !$email_sent_successfully ) {
- throw Error::Simple(
- "Could not send email to '$recipient_email_address' "
- . "using either EvEl or local MTA."
- );
- }
-
-}
-
-# send_confirmation_email TO (NAME) TEMPLATE-NAME PARAMETERS
-# TEMPLATE-NAME is currently one of problem, update, alert, tms
-sub send_confirmation_email {
- my ($q, $recipient_email_address, $name, $thing, %h) = @_;
-
- my $file_thing = $thing;
- $file_thing = 'empty property' if $q->{site} eq 'emptyhomes' && $thing eq 'problem'; # Needs to be in English
- my $template = "$file_thing-confirm";
-
- send_email($q, $recipient_email_address, $name, $template, %h);
-
- my ($action, $worry);
- if ($thing eq 'problem') {
- $action = _('your problem will not be posted');
- $worry = _("we'll hang on to your problem report while you're checking your email.");
- } elsif ($thing eq 'update') {
- $action = _('your update will not be posted');
- $worry = _("we'll hang on to your update while you're checking your email.");
- } elsif ($thing eq 'alert') {
- $action = _('your alert will not be activated');
- $worry = _("we'll hang on to your alert while you're checking your email.");
- } elsif ($thing eq 'tms') {
- $action = 'your expression of interest will not be registered';
- $worry = "we'll hang on to your expression of interest while you're checking your email.";
- }
-
- my $out = sprintf(_(<<EOF), $action, $worry);
-<h1>Nearly Done! Now check your email...</h1>
-<p>The confirmation email <strong>may</strong> take a few minutes to arrive &mdash; <em>please</em> be patient.</p>
-<p>If you use web-based email or have 'junk mail' filters, you may wish to check your bulk/spam mail folders: sometimes, our messages are marked that way.</p>
-<p>You must now click the link in the email we've just sent you &mdash;
-if you do not, %s.</p>
-<p>(Don't worry &mdash; %s)</p>
-EOF
-
- my $cobrand = get_cobrand($q);
- my %vars = (
- action => $action,
- worry => $worry,
- url_home => Cobrand::url($cobrand, '/', $q),
- );
- my $cobrand_email = Page::template_include('check-email', $q, Page::template_root($q), %vars);
- return $cobrand_email if $cobrand_email;
- return $out;
-}
-
-sub prettify_epoch {
- my ($q, $s, $short) = @_;
- my $cobrand = get_cobrand($q);
- my $cobrand_datetime = Cobrand::prettify_epoch($cobrand, $s);
- return $cobrand_datetime if ($cobrand_datetime);
- my @s = localtime($s);
- my $tt = strftime('%H:%M', @s);
- my @t = localtime();
- if (strftime('%Y%m%d', @s) eq strftime('%Y%m%d', @t)) {
- $tt = "$tt " . _('today');
- } elsif (strftime('%Y %U', @s) eq strftime('%Y %U', @t)) {
- $tt = "$tt, " . decode_utf8(strftime('%A', @s));
- } elsif ($short) {
- $tt = "$tt, " . decode_utf8(strftime('%e %b %Y', @s));
- } elsif (strftime('%Y', @s) eq strftime('%Y', @t)) {
- $tt = "$tt, " . decode_utf8(strftime('%A %e %B %Y', @s));
- } else {
- $tt = "$tt, " . decode_utf8(strftime('%a %e %B %Y', @s));
- }
- return $tt;
-}
-
-# argument is duration in seconds, rounds to the nearest minute
-sub prettify_duration {
- my ($s, $nearest) = @_;
- if ($nearest eq 'week') {
- $s = int(($s+60*60*24*3.5)/60/60/24/7)*60*60*24*7;
- } elsif ($nearest eq 'day') {
- $s = int(($s+60*60*12)/60/60/24)*60*60*24;
- } elsif ($nearest eq 'hour') {
- $s = int(($s+60*30)/60/60)*60*60;
- } elsif ($nearest eq 'minute') {
- $s = int(($s+30)/60)*60;
- return _('less than a minute') if $s == 0;
- }
- my @out = ();
- _part(\$s, 60*60*24*7, _('%d week'), _('%d weeks'), \@out);
- _part(\$s, 60*60*24, _('%d day'), _('%d days'), \@out);
- _part(\$s, 60*60, _('%d hour'), _('%d hours'), \@out);
- _part(\$s, 60, _('%d minute'), _('%d minutes'), \@out);
- return join(', ', @out);
-}
-sub _part {
- my ($s, $m, $w1, $w2, $o) = @_;
- if ($$s >= $m) {
- my $i = int($$s / $m);
- push @$o, sprintf(mySociety::Locale::nget($w1, $w2, $i), $i);
- $$s -= $i * $m;
- }
-}
-
-sub display_problem_meta_line($$) {
- my ($q, $problem) = @_;
- my $out = '';
- my $date_time = prettify_epoch($q, $problem->{time});
- if ($q->{site} eq 'emptyhomes') {
- my $category = _($problem->{category});
- utf8::decode($category); # So that Welsh to Welsh doesn't encode already-encoded UTF-8
- if ($problem->{anonymous}) {
- $out .= sprintf(_('%s, reported anonymously at %s'), ent($category), $date_time);
- } else {
- $out .= sprintf(_('%s, reported by %s at %s'), ent($category), ent($problem->{name}), $date_time);
- }
- } else {
- if ($problem->{service} && $problem->{category} && $problem->{category} ne _('Other') && $problem->{anonymous}) {
- $out .= sprintf(_('Reported by %s in the %s category anonymously at %s'), ent($problem->{service}), ent($problem->{category}), $date_time);
- } elsif ($problem->{service} && $problem->{category} && $problem->{category} ne _('Other')) {
- $out .= sprintf(_('Reported by %s in the %s category by %s at %s'), ent($problem->{service}), ent($problem->{category}), ent($problem->{name}), $date_time);
- } elsif ($problem->{service} && $problem->{anonymous}) {
- $out .= sprintf(_('Reported by %s anonymously at %s'), ent($problem->{service}), $date_time);
- } elsif ($problem->{service}) {
- $out .= sprintf(_('Reported by %s by %s at %s'), ent($problem->{service}), ent($problem->{name}), $date_time);
- } elsif ($problem->{category} && $problem->{category} ne _('Other') && $problem->{anonymous}) {
- $out .= sprintf(_('Reported in the %s category anonymously at %s'), ent($problem->{category}), $date_time);
- } elsif ($problem->{category} && $problem->{category} ne _('Other')) {
- $out .= sprintf(_('Reported in the %s category by %s at %s'), ent($problem->{category}), ent($problem->{name}), $date_time);
- } elsif ($problem->{anonymous}) {
- $out .= sprintf(_('Reported anonymously at %s'), $date_time);
- } else {
- $out .= sprintf(_('Reported by %s at %s'), ent($problem->{name}), $date_time);
- }
- }
- my $cobrand = get_cobrand($q);
- $out .= Cobrand::extra_problem_meta_text($cobrand, $problem);
- $out .= '; ' . _('the map was not used so pin location may be inaccurate') unless ($problem->{used_map});
- if ($problem->{council}) {
- if ($problem->{whensent}) {
- my $body;
- if ($problem->{external_body}) {
- $body = $problem->{external_body};
- } else {
- $problem->{council} =~ s/\|.*//g;
- my @councils = split /,/, $problem->{council};
- my $areas_info = mySociety::MaPit::call('areas', \@councils);
- $body = join(' and ', map { $areas_info->{$_}->{name} } @councils);
- }
- $out .= '<small class="council_sent_info">';
- $out .= $q->br() . sprintf(_('Sent to %s %s later'), $body, prettify_duration($problem->{whensent}, 'minute'));
- $out .= '</small>';
- }
- } else {
- $out .= $q->br() . $q->small(_('Not reported to council'));
- }
- return $out;
-}
-
-sub display_problem_detail($) {
- my $problem = shift;
- (my $detail = $problem->{detail}) =~ s/\r//g;
- my $out = '';
- foreach (split /\n{2,}/, $detail) {
- my $enttext = $_;
- $enttext =~ s%(https?://[^\s]+)%<a href="$1">$1</a>%g;
- $out .= '<p>' . $enttext . '</p>';
- }
- return $out;
-}
-
-sub display_problem_photo($$) {
- my ($q, $problem) = @_;
- my $cobrand = get_cobrand($q);
- my $display_photos = Cobrand::allow_photo_display($cobrand);
- if ($display_photos && $problem->{photo}) {
- my $dims = Image::Size::html_imgsize(\$problem->{photo});
- return "<p align='center'><img alt='' $dims src='/photo?id=$problem->{id}'></p>";
- }
- return '';
-}
-
-# Display information about problem
-sub display_problem_text($$) {
- my ($q, $problem) = @_;
-
- my $out = $q->h1(ent($problem->{title}));
- $out .= '<p><em>';
- $out .= display_problem_meta_line($q, $problem);
- $out .= '</em></p>';
- $out .= display_problem_detail($problem);
- $out .= display_problem_photo($q, $problem);
- return $out;
-}
-
-# Display updates
-sub display_problem_updates($$) {
- my ($id, $q) = @_;
- my $cobrand = get_cobrand($q);
- my $updates = select_all(
- "select id, name, extract(epoch from confirmed) as confirmed, text,
- mark_fixed, mark_open, photo, cobrand
- from comment where problem_id = ? and state='confirmed'
- order by confirmed", $id);
- my $out = '';
- if (@$updates) {
- $out .= '<div id="updates">';
- $out .= '<h2 class="problem-update-list-header">' . _('Updates') . '</h2>';
- foreach my $row (@$updates) {
- $out .= "<div><div class=\"problem-update\"><p><a name=\"update_$row->{id}\"></a><em>";
- if ($row->{name}) {
- $out .= sprintf(_('Posted by %s at %s'), ent($row->{name}), prettify_epoch($q, $row->{confirmed}));
- } else {
- $out .= sprintf(_('Posted anonymously at %s'), prettify_epoch($q, $row->{confirmed}));
- }
- $out .= Cobrand::extra_update_meta_text($cobrand, $row);
- $out .= ', ' . _('marked as fixed') if ($row->{mark_fixed});
- $out .= ', ' . _('reopened') if ($row->{mark_open});
- $out .= '</em></p>';
-
- my $allow_update_reporting = Cobrand::allow_update_reporting($cobrand);
- if ($allow_update_reporting) {
- my $contact = '/contact?id=' . $id . ';update_id='. $row->{id};
- my $contact_url = Cobrand::url($cobrand, $contact, $q);
- $out .= '<p>';
- $out .= $q->a({rel => 'nofollow', class => 'unsuitable-problem', href => $contact_url}, _('Offensive? Unsuitable? Tell us'));
- $out .= '</p>';
- }
- $out .= '</div>';
- $out .= '<div class="update-text">';
- my $text = $row->{text};
- $text =~ s/\r//g;
- foreach (split /\n{2,}/, $text) {
- my $enttext = ent($_);
- $enttext =~ s%(https?://[^\s]+)%<a href="$1">$1</a>%g;
- $out .= '<p>' . $enttext . '</p>';
- }
- my $cobrand = get_cobrand($q);
- my $display_photos = Cobrand::allow_photo_display($cobrand);
- if ($display_photos && $row->{photo}) {
- my $dims = Image::Size::html_imgsize(\$row->{photo});
- $out .= "<p><img alt='' $dims src='/photo?c=$row->{id}'></p>";
- }
- $out .= '</div>';
- $out .= '</div>';
- }
- $out .= '</div>';
- }
- return $out;
-}
-
-sub mapit_check_error {
- my $location = shift;
- if ($location->{error}) {
- return _('That postcode was not recognised, sorry.') if $location->{code} =~ /^4/;
- return $location->{error};
- }
- if (mySociety::Config::get('COUNTRY') eq 'GB') {
- my $island = $location->{coordsyst};
- if (!$island) {
- return _("Sorry, that appears to be a Crown dependency postcode, which we don't cover.");
- }
- if ($island eq 'I') {
- return _("We do not cover Northern Ireland, I'm afraid, as our licence doesn't include any maps for the region.");
- }
- }
- return 0;
-}
-
-sub short_name {
- my ($area, $info) = @_;
- # Special case Durham as it's the only place with two councils of the same name
- # And some places in Norway
- return 'Durham+County' if $area->{name} eq 'Durham County Council';
- return 'Durham+City' if $area->{name} eq 'Durham City Council';
- if ($area->{name} =~ /^(Os|Nes|V\xe5ler|Sande|B\xf8|Her\xf8y)$/) {
- my $parent = $info->{$area->{parent_area}}->{name};
- return URI::Escape::uri_escape_utf8("$area->{name}, $parent");
- }
- my $name = $area->{name};
- $name =~ s/ (Borough|City|District|County) Council$//;
- $name =~ s/ Council$//;
- $name =~ s/ & / and /;
- $name = URI::Escape::uri_escape_utf8($name);
- $name =~ s/%20/+/g;
- return $name;
-}
-
-sub check_photo {
- my ($q, $fh) = @_;
- my $ct = $q->uploadInfo($fh)->{'Content-Type'};
- my $cd = $q->uploadInfo($fh)->{'Content-Disposition'};
- # Must delete photo param, otherwise display functions get confused
- $q->delete('photo');
- return _('Please upload a JPEG image only') unless
- ($ct eq 'image/jpeg' || $ct eq 'image/pjpeg');
- return '';
-}
-
-sub process_photo {
- my $fh = shift;
- my $import = shift;
-
- my $blob = join('', <$fh>);
- close $fh;
- my ($handle, $filename) = mySociety::TempFiles::named_tempfile('.jpeg');
- print $handle $blob;
- close $handle;
-
- my $photo = Image::Magick->new;
- my $err = $photo->Read($filename);
- unlink $filename;
- throw Error::Simple("read failed: $err") if "$err";
- $err = $photo->Scale(geometry => "250x250>");
- throw Error::Simple("resize failed: $err") if "$err";
- my @blobs = $photo->ImageToBlob();
- undef $photo;
- $photo = $blobs[0];
- return $photo unless $import; # Only check orientation for iPhone imports at present
-
- # Now check if it needs orientating
- ($fh, $filename) = mySociety::TempFiles::named_tempfile('.jpeg');
- print $fh $photo;
- close $fh;
- my $out = `jhead -se -autorot $filename`;
- if ($out) {
- open(FP, $filename) or throw Error::Simple($!);
- $photo = join('', <FP>);
- close FP;
- }
- unlink $filename;
- return $photo;
-}
-
-sub scambs_categories {
- return ('Abandoned vehicles', 'Discarded hypodermic needles',
- 'Dog fouling', 'Flytipping', 'Graffiti', 'Lighting (e.g. security lights)',
- 'Litter', 'Neighbourhood noise');
-}
-
-1;
diff --git a/perllib/Problems.pm b/perllib/Problems.pm
deleted file mode 100644
index 7155aa485..000000000
--- a/perllib/Problems.pm
+++ /dev/null
@@ -1,589 +0,0 @@
-#!/usr/bin/perl
-#
-# Problems.pm:
-# Various problem report database fetching related functions for FixMyStreet.
-#
-# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/
-#
-# $Id: Problems.pm,v 1.33 2010-01-20 11:09:45 matthew Exp $
-#
-
-package Problems;
-
-use strict;
-use Encode;
-use Memcached;
-use mySociety::DBHandle qw/dbh select_all/;
-use mySociety::Locale;
-use mySociety::Web qw/ent/;
-use mySociety::MaPit;
-
-my $site_restriction = '';
-my $site_key = 0;
-
-sub set_site_restriction {
- my $q = shift;
- my $site = $q->{site};
- if ($site ne 'fixmystreet'){
- my $cobrand = Page::get_cobrand($q);
- my $cobrand_data = Cobrand::extra_data($cobrand, $q);
- ($site_restriction, $site_key) = Cobrand::site_restriction($cobrand, $cobrand_data);
- } else {
- $site_restriction = '';
- $site_key = 0;
- }
-}
-
-sub current_timestamp {
- my $current_timestamp = dbh()->selectrow_array('select ms_current_timestamp()');
- return "'$current_timestamp'::timestamp";
-}
-
-# Front page statistics
-
-sub recent_fixed {
- my $key = "recent_fixed:$site_key";
- my $result = Memcached::get($key);
- unless ($result) {
- $result = dbh()->selectrow_array("select count(*) from problem
- where state='fixed' and lastupdate>" . current_timestamp() . "-'1 month'::interval
- $site_restriction");
- Memcached::set($key, $result, 3600);
- }
- return $result;
-}
-
-sub number_comments {
- my $key = "number_comments:$site_key";
- my $result = Memcached::get($key);
- unless ($result) {
- if ($site_restriction) {
- $result = dbh()->selectrow_array("select count(*) from comment, problem
- where comment.problem_id=problem.id and comment.state='confirmed'
- $site_restriction");
- } else {
- $result = dbh()->selectrow_array("select count(*) from comment
- where state='confirmed'");
- }
- Memcached::set($key, $result, 3600);
- }
- return $result;
-}
-
-sub recent_new {
- my $interval = shift;
- (my $key = $interval) =~ s/\s+//g;
- $key = "recent_new:$site_key:$key";
- my $result = Memcached::get($key);
- unless ($result) {
- $result = dbh()->selectrow_array("select count(*) from problem
- where state in ('confirmed','fixed') and confirmed>" . current_timestamp() . "-'$interval'::interval
- $site_restriction");
- Memcached::set($key, $result, 3600);
- }
- return $result;
-}
-
-# Front page recent lists
-
-sub recent_photos {
- my ($num, $lat, $lon, $dist) = @_;
- my $probs;
- if (defined $lat) {
- my $dist2 = $dist; # Create a copy of the variable to stop it being stringified into a locale in the next line!
- my $key = "recent_photos:$site_key:$num:$lat:$lon:$dist2";
- $probs = Memcached::get($key);
- unless ($probs) {
- $probs = mySociety::Locale::in_gb_locale {
- select_all("select id, title
- from problem_find_nearby(?, ?, ?) as nearby, problem
- where nearby.problem_id = problem.id
- and state in ('confirmed', 'fixed') and photo is not null
- $site_restriction
- order by confirmed desc limit $num", $lat, $lon, $dist);
- };
- Memcached::set($key, $probs, 3600);
- }
- } else {
- my $key = "recent_photos:$site_key:$num";
- $probs = Memcached::get($key);
- unless ($probs) {
- $probs = select_all("select id, title from problem
- where state in ('confirmed', 'fixed') and photo is not null
- $site_restriction
- order by confirmed desc limit $num");
- Memcached::set($key, $probs, 3600);
- }
- }
- my $out = '';
- foreach (@$probs) {
- my $title = ent($_->{title});
- $out .= '<a href="/report/' . $_->{id} .
- '"><img border="0" height="100" src="/photo?tn=1;id=' . $_->{id} .
- '" alt="' . $title . '" title="' . $title . '"></a>';
- }
- return $out;
-}
-
-sub recent {
- my $key = "recent:$site_key";
- my $result = Memcached::get($key);
- unless ($result) {
- $result = select_all("select id,title from problem
- where state in ('confirmed', 'fixed')
- $site_restriction
- order by confirmed desc limit 5");
- Memcached::set($key, $result, 3600);
- }
- return $result;
-}
-
-sub front_stats {
- my ($q) = @_;
- my $fixed = Problems::recent_fixed();
- my $updates = Problems::number_comments();
- my $new = Problems::recent_new('1 week');
- (my $new_pretty = $new) =~ s/(?<=\d)(?=(?:\d\d\d)+$)/,/g;
- my $new_text = sprintf(mySociety::Locale::nget('<big>%s</big> report in past week',
- '<big>%s</big> reports in past week', $new), $new_pretty);
- if ($q->{site} ne 'emptyhomes' && $new > $fixed) {
- $new = Problems::recent_new('3 days');
- ($new_pretty = $new) =~ s/(?<=\d)(?=(?:\d\d\d)+$)/,/g;
- $new_text = sprintf(mySociety::Locale::nget('<big>%s</big> report recently', '<big>%s</big> reports recently', $new), $new_pretty);
- }
- (my $fixed_pretty = $fixed) =~ s/(?<=\d)(?=(?:\d\d\d)+$)/,/g;
- (my $updates_pretty = $updates) =~ s/(?<=\d)(?=(?:\d\d\d)+$)/,/g;
-
- my $out = '';
- $out .= $q->h2(_('FixMyStreet updates'));
- my $lastmo = '';
- if ($q->{site} ne 'emptyhomes'){
- $lastmo = $q->div(sprintf(mySociety::Locale::nget("<big>%s</big> fixed in past month", "<big>%s</big> fixed in past month", $fixed), $fixed), $fixed_pretty);
- }
- $out .= $q->div({-id => 'front_stats'},
- $q->div($new_text),
- ($q->{site} ne 'emptyhomes' ? $q->div(sprintf(mySociety::Locale::nget("<big>%s</big> fixed in past month", "<big>%s</big> fixed in past month", $fixed), $fixed_pretty)) : ''),
- $q->div(sprintf(mySociety::Locale::nget("<big>%s</big> update on reports",
- "<big>%s</big> updates on reports", $updates), $updates_pretty))
- );
- return $out;
-
-}
-
-# Problems around a location
-
-sub around_map {
- my ($min_lat, $max_lat, $min_lon, $max_lon, $interval, $limit) = @_;
- my $limit_clause = '';
- if ($limit) {
- $limit_clause = " limit $limit";
- }
- mySociety::Locale::in_gb_locale { select_all(
- "select id,title,latitude,longitude,state,
- extract(epoch from confirmed) as time
- from problem
- where state in ('confirmed', 'fixed')
- and latitude>=? and latitude<? and longitude>=? and longitude<? " .
- ($interval ? " and ms_current_timestamp()-lastupdate < '$interval'::interval" : '') .
- " $site_restriction
- order by created desc
- $limit_clause", $min_lat, $max_lat, $min_lon, $max_lon);
- };
-}
-
-sub nearby {
- my ($dist, $ids, $limit, $mid_lat, $mid_lon, $interval) = @_;
- mySociety::Locale::in_gb_locale { select_all(
- "select id, title, latitude, longitude, distance, state,
- extract(epoch from confirmed) as time
- from problem_find_nearby(?, ?, $dist) as nearby, problem
- where nearby.problem_id = problem.id " .
- ($interval ? " and ms_current_timestamp()-lastupdate < '$interval'::interval" : '') .
- " and state in ('confirmed', 'fixed')" . ($ids ? ' and id not in (' . $ids . ')' : '') . "
- $site_restriction
- order by distance, created desc limit $limit", $mid_lat, $mid_lon);
- }
-}
-
-sub fixed_nearby {
- my ($dist, $mid_lat, $mid_lon) = @_;
- mySociety::Locale::in_gb_locale { select_all(
- "select id, title, latitude, longitude, distance
- from problem_find_nearby(?, ?, $dist) as nearby, problem
- where nearby.problem_id = problem.id and state='fixed'
- $site_restriction
- order by lastupdate desc", $mid_lat, $mid_lon);
- }
-}
-
-# Fetch an individual problem
-
-sub fetch_problem {
- my $id = shift;
- my $p = dbh()->selectrow_hashref(
- "select id, latitude, longitude, council, category, title, detail, photo,
- used_map, name, anonymous, extract(epoch from confirmed) as time,
- state, extract(epoch from whensent-confirmed) as whensent,
- extract(epoch from ms_current_timestamp()-lastupdate) as duration,
- service, cobrand, cobrand_data, external_body
- from problem where id=? and state in ('confirmed','fixed', 'hidden')
- $site_restriction", {}, $id
- );
- $p->{service} =~ s/_/ /g if $p && $p->{service};
- return $p;
-}
-
-# API functions
-
-sub problems_matching_criteria {
- my ($criteria, @params) = @_;
- my $problems = select_all(
- "select id, title, council, category, detail, name, anonymous,
- confirmed, whensent, service
- from problem
- $criteria
- $site_restriction", @params);
-
- my @councils;
- foreach my $problem (@$problems){
- if ($problem->{anonymous} == 1){
- $problem->{name} = '';
- }
- if ($problem->{service} eq ''){
- $problem->{service} = 'Web interface';
- }
- if ($problem->{council}) {
- $problem->{council} =~ s/\|.*//g;
- my @council_ids = split /,/, $problem->{council};
- push(@councils, @council_ids);
- $problem->{council} = \@council_ids;
- }
- }
- my $areas_info = mySociety::MaPit::call('areas', \@councils);
- foreach my $problem (@$problems){
- if ($problem->{council}) {
- my @council_names = map { $areas_info->{$_}->{name} } @{$problem->{council}} ;
- $problem->{council} = join(' and ', @council_names);
- }
- }
- return $problems;
-}
-
-sub fixed_in_interval {
- my ($start_date, $end_date) = @_;
- my $criteria = "where state='fixed' and date_trunc('day',lastupdate)>=? and
-date_trunc('day',lastupdate)<=?";
- return problems_matching_criteria($criteria, $start_date, $end_date);
-}
-
-sub created_in_interval {
- my ($start_date, $end_date) = @_;
- my $criteria = "where state='confirmed' and date_trunc('day',created)>=? and
-date_trunc('day',created)<=?";
- return problems_matching_criteria($criteria, $start_date, $end_date);
-}
-
-=item data_sharing_notification_start
-
-Returns the unix datetime when the T&Cs that explicitly allow for users' data to be displayed
-on other sites.
-
-=cut
-
-sub data_sharing_notification_start {
- return 1255392000;
-}
-
-# Report functions
-
-=item council_problems WARD COUNCIL
-
-Returns a list of problems for a summary page. If WARD or COUNCIL area ids are given,
-will only return problems for that area. Uses any site restriction defined by the
-cobrand.
-
-=cut
-
-sub council_problems {
- my ($ward, $one_council) = @_;
- my @params;
- my $where_extra = '';
- if ($ward) {
- push @params, $ward;
- $where_extra = "and areas like '%,'||?||',%'";
- } elsif ($one_council) {
- push @params, $one_council;
- $where_extra = "and areas like '%,'||?||',%'";
- }
- my $current_timestamp = current_timestamp();
- my $problems = select_all(
- "select id, title, detail, council, state, areas,
- extract(epoch from $current_timestamp-lastupdate) as duration,
- extract(epoch from $current_timestamp-confirmed) as age
- from problem
- where state in ('confirmed', 'fixed')
- $where_extra
- $site_restriction
- order by id desc
- ", @params);
- return $problems;
-}
-
-# Admin view functions
-
-=item problem_search SEARCH
-
-Returns all problems containing the search term in their name, email, title,
-detail or council, or whose ID is the search term. Uses any site_restriction
-defined by a cobrand.
-
-=cut
-sub problem_search {
- my ($search) = @_;
- my $search_n = 0;
- $search_n = int($search) if $search =~ /^\d+$/;
- my $problems = select_all("select id, council, category, title, name,
- email, anonymous, cobrand, cobrand_data, created, confirmed, state, service, lastupdate,
- whensent, send_questionnaire from problem where (id=? or email ilike
- '%'||?||'%' or name ilike '%'||?||'%' or title ilike '%'||?||'%' or
- detail ilike '%'||?||'%' or council like '%'||?||'%' or cobrand_data like '%'||?||'%')
- $site_restriction
- order by (state='hidden'),created", $search_n,
- $search, $search, $search, $search, $search, $search);
- return $problems;
-}
-
-=item update_search SEARCH
-
-Returns all updates containing the search term in their name, email or text, or whose ID
-is the search term. Uses any site_restriction defined by a cobrand.
-
-=cut
-sub update_search {
- my ($search) = @_;
- my $search_n = 0;
- $search_n = int($search) if $search =~ /^\d+$/;
- my $updates = select_all("select comment.*, problem.council, problem.state as problem_state
- from comment, problem where problem.id = comment.problem_id
- and (comment.id=? or
- problem_id=? or comment.email ilike '%'||?||'%' or comment.name ilike '%'||?||'%' or
- comment.text ilike '%'||?||'%' or comment.cobrand_data ilike '%'||?||'%')
- $site_restriction
- order by (comment.state='hidden'),(problem.state='hidden'),created", $search_n, $search_n, $search, $search,
- $search, $search);
- return $updates;
-}
-
-=item update_counts
-
-An array reference of updates grouped by state. Uses any site_restriction defined by a cobrand.
-
-=cut
-
-sub update_counts {
- return dbh()->selectcol_arrayref("select comment.state, count(comment.*) as c from comment, problem
- where problem.id = comment.problem_id
- $site_restriction
- group by comment.state", { Columns => [1,2] });
-}
-
-=item problem_counts
-
-An array reference of problems grouped by state. Uses any site_restriction defined by a cobrand.
-
-=cut
-
-sub problem_counts {
- return dbh()->selectcol_arrayref("select state, count(*) as c from problem
- where id=id $site_restriction
- group by state", { Columns => [1,2] });
-}
-
-=item
-
-An array reference of alerts grouped by state (specific to the cobrand if there is one).
-
-=cut
-
-sub alert_counts {
- my ($cobrand) = @_;
- my $cobrand_clause = '';
- if ($cobrand) {
- $cobrand_clause = " where cobrand = '$cobrand'";
- }
- return dbh()->selectcol_arrayref("select confirmed, count(*) as c
- from alert
- $cobrand_clause
- group by confirmed", { Columns => [1,2] });
-}
-
-=item questionnaire_counts
-
-An array reference of questionnaires. Restricted to questionnaires related to
-problems submitted through the cobrand if a cobrand is specified.
-
-=cut
-sub questionnaire_counts {
- my ($cobrand) = @_;
- my $cobrand_clause = '';
- if ($cobrand) {
- $cobrand_clause = " and cobrand = '$cobrand'";
- }
- my $questionnaires = dbh()->selectcol_arrayref("select (whenanswered is not null), count(questionnaire.*) as c
- from questionnaire, problem
- where problem.id = questionnaire.problem_id
- $cobrand_clause
- group by (whenanswered is not null)", { Columns => [1,2] });
- return $questionnaires;
-}
-
-=item contact_counts COBRAND
-
-An array reference of contacts. Restricted to contacts relevant to
-the cobrand if a cobrand is specified.
-
-=cut
-sub contact_counts {
- my ($cobrand) = @_;
- my $contact_restriction = Cobrand::contact_restriction($cobrand);
- my $contacts = dbh()->selectcol_arrayref("select confirmed, count(*) as c from contacts $contact_restriction group by confirmed", { Columns => [1,2] });
- return $contacts;
-}
-
-=item admin_fetch_problem ID
-
-Return an array reference of data relating to a problem, to be used in the admin interface.
-Uses any site_restriction defined by a cobrand.
-
-=cut
-
-sub admin_fetch_problem {
- my ($id) = @_;
- my $problem = dbh()->selectall_arrayref("select * from problem
- where id=?
- $site_restriction", { Slice=>{} }, $id);
- return $problem;
-}
-
-=item admin_fetch_update ID
-
-Return an array reference of data relating to an update, to be used in the admin interface.
-Uses any site_restriction defined by a cobrand.
-
-=cut
-sub admin_fetch_update {
- my ($id) = @_;
- my $update = dbh()->selectall_arrayref("select comment.*, problem.council from comment, problem
- where comment.id=?
- and problem.id = comment.problem_id
- $site_restriction", { Slice=>{} }, $id);
- return $update;
-}
-
-=item timeline_problems
-
-Return a reference to an array of problems suitable for display in the admin timeline.
-Uses any site_restriction defined by a cobrand.
-=cut
-sub timeline_problems {
- my $current_timestamp = current_timestamp();
- my $problems = select_all("select state,id,name,email,title,council,category,service,cobrand,cobrand_data,
- extract(epoch from created) as created,
- extract(epoch from confirmed) as confirmed,
- extract(epoch from whensent) as whensent
- from problem where (created>=$current_timestamp-'7 days'::interval
- or confirmed>=$current_timestamp-'7 days'::interval
- or whensent>=$current_timestamp-'7 days'::interval)
- $site_restriction");
- return $problems;
-
-}
-
-=item timeline_updates
-
-Return a reference to an array of updates suitable for display in the admin timeline.
-Uses any site_restriction defined by a cobrand.
-
-=cut
-
-sub timeline_updates {
- my $updates = select_all("select comment.*,
- extract(epoch from comment.created) as created,
- problem.council
- from comment, problem
- where comment.problem_id = problem.id
- and comment.state='confirmed'
- and comment.created>=" . current_timestamp() . "-'7 days'::interval
- $site_restriction");
- return $updates;
-}
-
-=item timeline_alerts COBRAND
-
-Return a reference to an array of alerts suitable for display in the admin timeline. Restricted to
-cobranded alerts if a cobrand is specified.
-
-=cut
-sub timeline_alerts {
- my ($cobrand) = @_;
- my $cobrand_clause = '';
- if ($cobrand) {
- $cobrand_clause = " and cobrand = '$cobrand'";
- }
- my $alerts = select_all("select *,
- extract(epoch from whensubscribed) as whensubscribed
- from alert where whensubscribed>=" . current_timestamp() . "-'7 days'::interval
- and confirmed=1
- $cobrand_clause");
- return $alerts;
-
-}
-
-=item timeline_deleted_alerts COBRAND
-
-Return a reference to an array of deleted alerts suitable for display in the admin timeline. Restricted to
-cobranded alerts if a cobrand is specified.
-
-=cut
-sub timeline_deleted_alerts {
- my ($cobrand) = @_;
- my $cobrand_clause = '';
- if ($cobrand) {
- $cobrand_clause = " and cobrand = '$cobrand'";
- }
-
- my $alerts = select_all("select *,
- extract(epoch from whensubscribed) as whensubscribed,
- extract(epoch from whendisabled) as whendisabled
- from alert where whendisabled>=" . current_timestamp() . "-'7 days'::interval
- $cobrand_clause");
- return $alerts;
-
-}
-
-=item timeline_questionnaires
-
-Return a reference to an array of questionnaires suitable for display in the admin timeline. Restricted to
-questionnaires for cobranded problems if a cobrand is specified.
-
-=cut
-
-sub timeline_questionnaires {
- my ($cobrand) = @_;
- my $cobrand_clause = '';
- if ($cobrand) {
- $cobrand_clause = " and cobrand = '$cobrand'";
- }
- my $current_timestamp = current_timestamp();
- my $questionnaire = select_all("select questionnaire.*,
- extract(epoch from questionnaire.whensent) as whensent,
- extract(epoch from questionnaire.whenanswered) as whenanswered
- from questionnaire, problem
- where questionnaire.problem_id = problem.id
- and (questionnaire.whensent>=$current_timestamp-'7 days'::interval
- or questionnaire.whenanswered>=$current_timestamp-'7 days'::interval)
- $cobrand_clause");
-}
-
-1;
diff --git a/perllib/Standard.pm b/perllib/Standard.pm
deleted file mode 100644
index 571065c14..000000000
--- a/perllib/Standard.pm
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/perl
-#
-# Standard.pm:
-# Common headers for Perl files. Mostly in the main namespace on purpose
-# (Filter::Macro sadly didn't work, CPAN bug #20494)
-#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/
-#
-# $Id: Standard.pm,v 1.3 2009-09-15 13:57:01 louise Exp $
-
-use strict;
-use warnings;
-require 5.8.0;
-
-# Horrible boilerplate to set up appropriate library paths.
-use FindBin;
-use lib "$FindBin::Bin/../perllib";
-use lib "$FindBin::Bin/../commonlib/perllib";
-
-use Page;
-
-package Standard;
-
-sub import {
- my $package = shift;
- my $db = shift;
- unless ($db && $db eq '-db') {
- use mySociety::Config;
- use mySociety::DBHandle qw(dbh);
- (my $file = __FILE__) =~ s{/[^/]*?$}{};
- mySociety::Config::set_file("$file/../conf/general");
- mySociety::DBHandle::configure(
- Name => mySociety::Config::get('BCI_DB_NAME'),
- User => mySociety::Config::get('BCI_DB_USER'),
- Password => mySociety::Config::get('BCI_DB_PASS'),
- Host => mySociety::Config::get('BCI_DB_HOST', undef),
- Port => mySociety::Config::get('BCI_DB_PORT', undef)
- );
- *main::dbh = \&dbh;
- }
-}
diff --git a/perllib/Utils.pm b/perllib/Utils.pm
index c16a02cd4..bf8ac2805 100644
--- a/perllib/Utils.pm
+++ b/perllib/Utils.pm
@@ -12,6 +12,8 @@
package Utils;
use strict;
+use Encode;
+use POSIX qw(strftime);
use mySociety::DBHandle qw(dbh);
use mySociety::GeoUtil;
use mySociety::Locale;
@@ -136,4 +138,122 @@ sub london_categories {
};
}
+=head2 trim_text
+
+ my $text = trim_text( $text_to_trim );
+
+Strip leading and trailing white space from a string. Also reduces all
+white space to a single space.
+
+Trim
+
+=cut
+
+sub trim_text {
+ my $input = shift;
+ for ($input) {
+ last unless $_;
+ s{\s+}{ }g; # all whitespace to single space
+ s{^ }{}; # trim leading
+ s{ $}{}; # trim trailing
+ }
+ return $input;
+}
+
+
+=head2 cleanup_text
+
+Tidy up text including removing contentious phrases,
+SHOUTING and new lines and adding sentence casing. Takes an optional HASHREF
+of args as follows.
+
+=over
+
+=item allow_multiline
+
+Do not flatten down to a single line if true.
+
+=back
+
+=cut
+
+sub cleanup_text {
+ my $input = shift || '';
+ my $args = shift || {};
+
+ # lowercase everything if looks like it might be SHOUTING
+ $input = lc $input if $input !~ /[a-z]/;
+
+ # clean up language and tradmarks
+ for ($input) {
+
+ # shit -> poo
+ s{\bdog\s*shit\b}{dog poo}ig;
+
+ # 'portakabin' to '[portable cabin]' (and variations)
+ s{\b(porta)\s*([ck]abin|loo)\b}{[$1ble $2]}ig;
+ s{kabin\]}{cabin\]}ig;
+ }
+
+ # Remove unneeded whitespace
+ my @lines = grep { m/\S/ } split m/\n\n/, $input;
+ for (@lines) {
+ $_ = trim_text($_);
+ $_ = ucfirst $_; # start with capital
+ }
+
+ my $join_char = $args->{allow_multiline} ? "\n\n" : " ";
+ $input = join $join_char, @lines;
+
+ return $input;
+}
+
+sub prettify_epoch {
+ my ($s, $short) = @_;
+ my @s = localtime($s);
+ my $tt = strftime('%H:%M', @s);
+ my @t = localtime();
+ if (strftime('%Y%m%d', @s) eq strftime('%Y%m%d', @t)) {
+ $tt = "$tt " . _('today');
+ } elsif (strftime('%Y %U', @s) eq strftime('%Y %U', @t)) {
+ $tt = "$tt, " . decode_utf8(strftime('%A', @s));
+ } elsif ($short) {
+ $tt = "$tt, " . decode_utf8(strftime('%e %b %Y', @s));
+ } elsif (strftime('%Y', @s) eq strftime('%Y', @t)) {
+ $tt = "$tt, " . decode_utf8(strftime('%A %e %B %Y', @s));
+ } else {
+ $tt = "$tt, " . decode_utf8(strftime('%a %e %B %Y', @s));
+ }
+ return $tt;
+}
+
+# argument is duration in seconds, rounds to the nearest minute
+sub prettify_duration {
+ my ($s, $nearest) = @_;
+ if ($nearest eq 'week') {
+ $s = int(($s+60*60*24*3.5)/60/60/24/7)*60*60*24*7;
+ } elsif ($nearest eq 'day') {
+ $s = int(($s+60*60*12)/60/60/24)*60*60*24;
+ } elsif ($nearest eq 'hour') {
+ $s = int(($s+60*30)/60/60)*60*60;
+ } elsif ($nearest eq 'minute') {
+ $s = int(($s+30)/60)*60;
+ return _('less than a minute') if $s == 0;
+ }
+ my @out = ();
+ _part(\$s, 60*60*24*7, _('%d week'), _('%d weeks'), \@out);
+ _part(\$s, 60*60*24, _('%d day'), _('%d days'), \@out);
+ _part(\$s, 60*60, _('%d hour'), _('%d hours'), \@out);
+ _part(\$s, 60, _('%d minute'), _('%d minutes'), \@out);
+ return join(', ', @out);
+}
+sub _part {
+ my ($s, $m, $w1, $w2, $o) = @_;
+ if ($$s >= $m) {
+ my $i = int($$s / $m);
+ push @$o, sprintf(mySociety::Locale::nget($w1, $w2, $i), $i);
+ $$s -= $i * $m;
+ }
+}
+
1;
diff --git a/script/fixmystreet_app_create.pl b/script/fixmystreet_app_create.pl
new file mode 100755
index 000000000..c0ec0c898
--- /dev/null
+++ b/script/fixmystreet_app_create.pl
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('FixMyStreet::App', 'Create');
+
+1;
+
+=head1 NAME
+
+fixmystreet_app_create.pl - Create a new Catalyst Component
+
+=head1 SYNOPSIS
+
+fixmystreet_app_create.pl [options] model|view|controller name [helper] [options]
+
+ Options:
+ --force don't create a .new file where a file to be created exists
+ --mechanize use Test::WWW::Mechanize::Catalyst for tests if available
+ --help display this help and exits
+
+ Examples:
+ fixmystreet_app_create.pl controller My::Controller
+ fixmystreet_app_create.pl -mechanize controller My::Controller
+ fixmystreet_app_create.pl view My::View
+ fixmystreet_app_create.pl view HTML TT
+ fixmystreet_app_create.pl model My::Model
+ fixmystreet_app_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic\
+ dbi:SQLite:/tmp/my.db
+ fixmystreet_app_create.pl model AnotherDB DBIC::Schema MyApp::Schema create=static\
+ [Loader opts like db_schema, naming] dbi:Pg:dbname=foo root 4321
+ [connect_info opts like quote_char, name_sep]
+
+ See also:
+ perldoc Catalyst::Manual
+ perldoc Catalyst::Manual::Intro
+ perldoc Catalyst::Helper::Model::DBIC::Schema
+ perldoc Catalyst::Model::DBIC::Schema
+ perldoc Catalyst::View::TT
+
+=head1 DESCRIPTION
+
+Create a new Catalyst Component.
+
+Existing component files are not overwritten. If any of the component files
+to be created already exist the file will be written with a '.new' suffix.
+This behavior can be suppressed with the C<-force> option.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
diff --git a/script/fixmystreet_app_server.pl b/script/fixmystreet_app_server.pl
new file mode 100755
index 000000000..3778cf27d
--- /dev/null
+++ b/script/fixmystreet_app_server.pl
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+BEGIN {
+ $ENV{CATALYST_SCRIPT_GEN} = 40;
+}
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('FixMyStreet::App', 'Server');
+
+1;
+
+=head1 NAME
+
+fixmystreet_app_server.pl - Catalyst Test Server
+
+=head1 SYNOPSIS
+
+fixmystreet_app_server.pl [options]
+
+ -d --debug force debug mode
+ -f --fork handle each request in a new process
+ (defaults to false)
+ -? --help display this help and exits
+ -h --host host (defaults to all)
+ -p --port port (defaults to 3000)
+ -k --keepalive enable keep-alive connections
+ -r --restart restart when files get modified
+ (defaults to false)
+ -rd --restart_delay delay between file checks
+ (ignored if you have Linux::Inotify2 installed)
+ -rr --restart_regex regex match files that trigger
+ a restart when modified
+ (defaults to '\.yml$|\.yaml$|\.conf|\.pm$')
+ --restart_directory the directory to search for
+ modified files, can be set multiple times
+ (defaults to '[SCRIPT_DIR]/..')
+ --follow_symlinks follow symlinks in search directories
+ (defaults to false. this is a no-op on Win32)
+ --background run the process in the background
+ --pidfile specify filename for pid file
+
+ See also:
+ perldoc Catalyst::Manual
+ perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst Testserver for this application.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
diff --git a/script/fixmystreet_app_test.pl b/script/fixmystreet_app_test.pl
new file mode 100755
index 000000000..4eefd06a4
--- /dev/null
+++ b/script/fixmystreet_app_test.pl
@@ -0,0 +1,40 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('FixMyStreet::App', 'Test');
+
+1;
+
+=head1 NAME
+
+fixmystreet_app_test.pl - Catalyst Test
+
+=head1 SYNOPSIS
+
+fixmystreet_app_test.pl [options] uri
+
+ Options:
+ --help display this help and exits
+
+ Examples:
+ fixmystreet_app_test.pl http://localhost/some_action
+ fixmystreet_app_test.pl /some_action
+
+ See also:
+ perldoc Catalyst::Manual
+ perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst action from the command line.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
diff --git a/setenv.pl b/setenv.pl
new file mode 100755
index 000000000..2916a2c8a
--- /dev/null
+++ b/setenv.pl
@@ -0,0 +1,85 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use List::MoreUtils 'uniq';
+
+my $root;
+
+BEGIN { # add the local perllibs too
+
+ # Can't use Path::Class here as we'd load the old debian one.
+ $root = __FILE__ =~ m{^(.*)/(web|bin)/\.\..*$} ? $1 : `pwd`;
+ chomp($root);
+}
+
+# Set the environment for the FixMyStreet project
+
+# Add the lib/perl5 in perl-external so that we can load local::lib from there
+use lib "$root/perl-external/lib/perl5";
+
+# Add the perl-external dirs properly using local::lib
+use local::lib "$root/perl-external";
+use local::lib "$root/perl-external/local-lib";
+
+use lib "$root/commonlib/perllib";
+use lib "$root/perllib";
+for ( "$root/commonlib/perllib", "$root/perllib" ) {
+ $ENV{PERL5LIB} = "$_:$ENV{PERL5LIB}";
+}
+
+# also set the path to our scripts etc
+$ENV{PATH} = join ':', uniq "$root/bin", split( m/:/, $ENV{PATH} );
+
+# we might want to require this file to configure something like a CGI script
+if ( $0 eq __FILE__ ) {
+
+ my @keys = sort 'PATH', grep { m{^PERL} } keys %ENV;
+
+ print "export $_='$ENV{$_}'\n" for @keys;
+ print 'export PS1="(fms) $PS1"' . "\n";
+
+ print << "STOP";
+
+# $0 - set up the environment for FixMyStreet.
+#
+# This script can be used one of two ways:
+#
+# With arguments executes the arguments with the environment correctly set -
+# intended for things like the cron jobs:
+#
+# $0 env
+#
+# Or if no arguments prints out the bash shell commands needed to set up the
+# environment - which is useful when developing. Use this to set your current
+# shell:
+#
+# eval `$0`
+STOP
+
+}
+else {
+
+ # we were just required - unload some modules to prevent old code
+ # getting in the way of loading newer code from the newly set directories.
+
+ # use an eval to prevent needing Class::Unload before perl-external properly
+ # setup
+ eval "use Class::Unload";
+ die $@ if $@;
+
+ my @modules =
+ sort
+ grep { m/File::/ }
+ map { s{\.pm$}{}; s{/}{::}g; $_ }
+ grep { m{\.pm$} }
+ keys %INC;
+
+ for (@modules) {
+ Class::Unload->unload($_);
+ }
+}
+
+1;
+
diff --git a/t/Cobrand.t b/t/Cobrand.t
deleted file mode 100755
index 1f38ec605..000000000
--- a/t/Cobrand.t
+++ /dev/null
@@ -1,282 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Cobrand.t:
-# Tests for the cobranding functions
-#
-# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
-# Email: louise@mysociety.org; WWW: http://www.mysociety.org/
-#
-# $Id: Cobrand.t,v 1.25 2009-12-16 12:43:12 matthew Exp $
-#
-
-use strict;
-use warnings;
-use Test::More tests => 59;
-use Test::Exception;
-use Error qw(:try);
-
-use FindBin;
-use lib "$FindBin::Bin";
-use lib "$FindBin::Bin/../perllib";
-use lib "$FindBin::Bin/../commonlib/perllib";
-
-use Cobrand;
-use mySociety::MockQuery;
-
-sub test_site_restriction {
- my ($site_restriction, $site_id) = Cobrand::site_restriction('mysite', 'test');
- like($site_restriction, qr/ and council = 1 /, 'should return result of cobrand module site_restriction function');
- ok($site_id == 99, 'should return result of cobrand module site_restriction function');
-
- ($site_restriction, $site_id) = Cobrand::site_restriction('nosite', 'test');
- ok($site_restriction eq '', 'should return "" and zero if no module exists' );
- ok($site_id == 0, 'should return "" and zero if no module exists');
-}
-
-sub test_form_elements {
- my $q = new MockQuery('mysite');
- my $element_html = Cobrand::form_elements('mysite', 'postcodeForm', $q);
- ok($element_html eq 'Extra html', 'should return result of cobrand module element_html function') or diag("Got $element_html");
-
- $element_html = Cobrand::form_elements('nosite', 'postcodeForm', $q);
- ok($element_html eq '', 'should return an empty string if no cobrand module exists') or diag("Got $element_html");
-}
-
-sub test_disambiguate_location {
- my $q = new MockQuery('mysite');
- my $s = 'London Road';
- $s = Cobrand::disambiguate_location('mysite', $s, $q);
- ok($s eq 'Specific Location', 'should return result of cobrand module disambiguate_location function') or diag("Got $s");;
-
- $q = new MockQuery('nosite');
- $s = 'London Road';
- $s = Cobrand::disambiguate_location('nosite', $s, $q);
- ok($s eq 'London Road&gl=uk', 'should return location string as passed if no cobrand module exists') or diag("Got $s");
-
-}
-
-sub test_cobrand_handle {
- my $cobrand = 'mysite';
- my $handle = Cobrand::cobrand_handle($cobrand);
- like($handle->site_name(), qr/mysite/, 'should get a module handle if Util module exists for cobrand');
- $cobrand = 'nosite';
- $handle = Cobrand::cobrand_handle($cobrand);
- ok($handle == 0, 'should return zero if no module exists');
-}
-
-sub test_extra_problem_data {
- my $cobrand = 'mysite';
- my $q = new MockQuery($cobrand);
-
- # should get the result of the page function in the cobrand module if one exists
- my $cobrand_data = Cobrand::extra_problem_data($cobrand, $q);
- ok($cobrand_data eq 'Cobrand problem data', 'extra_problem_data should return data from cobrand module') or diag("Got $cobrand_data");
-
- # should return an empty string if no cobrand module exists
- $q = new MockQuery('nosite');
- $cobrand_data = Cobrand::extra_problem_data('nosite', $q);
- ok($cobrand_data eq '', 'extra_problem_data should return an empty string if there is no cobrand module') or diag("Got $cobrand_data");
-}
-
-sub test_extra_update_data {
- my $cobrand = 'mysite';
- my $q = new MockQuery($cobrand);
-
- # should get the result of the page function in the cobrand module if one exists
- my $cobrand_data = Cobrand::extra_update_data($cobrand, $q);
- ok($cobrand_data eq 'Cobrand update data', 'extra_update_data should return data from cobrand module') or diag("Got $cobrand_data");
-
- # should return an empty string if no cobrand module exists
- $q = new MockQuery('nosite');
- $cobrand_data = Cobrand::extra_update_data('nosite', $q);
- ok($cobrand_data eq '', 'extra_update_data should return an empty string if there is no cobrand module') or diag("Got $cobrand_data");
-}
-
-
-sub test_extra_alert_data {
- my $cobrand = 'mysite';
- my $q = new MockQuery($cobrand);
-
- # should get the result of the page function in the cobrand module if one exists
- my $cobrand_data = Cobrand::extra_alert_data($cobrand, $q);
- ok($cobrand_data eq 'Cobrand alert data', 'extra_alert_data should return data from cobrand module') or diag("Got $cobrand_data");
-
- # should return an empty string if no cobrand module exists
- $q = new MockQuery('nosite');
- $cobrand_data = Cobrand::extra_alert_data('nosite', $q);
- ok($cobrand_data eq '', 'extra_alert_data should return an empty string if there is no cobrand module') or diag("Got $cobrand_data");
-}
-
-sub test_base_url {
- my $cobrand = 'mysite';
-
- # should get the result of the page function in the cobrand module if one exists
- my $base_url = Cobrand::base_url($cobrand);
- is('http://mysite.example.com', $base_url, 'base_url returns output from cobrand module');
-
- # should return the base url from the config if there is no cobrand module
- $cobrand = 'nosite';
- $base_url = Cobrand::base_url($cobrand);
- is(mySociety::Config::get('BASE_URL'), $base_url, 'base_url returns config base url if no cobrand module');
-
-}
-
-sub test_base_url_for_emails {
- my $cobrand = 'mysite';
-
- # should get the results of the base_url_for_emails function in the cobrand module if one exists
- my $base_url = Cobrand::base_url_for_emails($cobrand);
- is('http://mysite.foremails.example.com', $base_url, 'base_url_for_emails returns output from cobrand module') ;
-
- # should return the result of Cobrand::base_url otherwise
- $cobrand = 'nosite';
- $base_url = Cobrand::base_url_for_emails($cobrand);
- is(mySociety::Config::get('BASE_URL'), $base_url, 'base_url_for_emails returns config base url if no cobrand module');
-
-}
-
-sub test_extra_params {
- my $cobrand = 'mysite';
- my $q = new MockQuery($cobrand);
-
- # should get the results of the extra_params function in the cobrand module if one exists
- my $extra_params = Cobrand::extra_params($cobrand, $q);
- is($extra_params, 'key=value', 'extra_params returns output from cobrand module') ;
-
- # should return an empty string otherwise
- $cobrand = 'nosite';
- $extra_params = Cobrand::extra_params($cobrand, $q);
- is($extra_params, '', 'extra_params returns an empty string if no cobrand module');
-
-}
-
-sub test_header_params {
- my $cobrand = 'mysite';
- my $q = new MockQuery($cobrand);
-
- # should get the results of the header_params function in the cobrand module if one exists
- my $header_params = Cobrand::header_params($cobrand, $q);
- is_deeply($header_params, {'key' => 'value'}, 'header_params returns output from cobrand module') ;
-
- # should return an empty string otherwise
- $cobrand = 'nosite';
- $header_params = Cobrand::header_params($cobrand, $q);
- is_deeply($header_params, {}, 'header_params returns an empty hash ref if no cobrand module');
-}
-
-sub test_root_path_js {
- my $cobrand = 'mysite';
- my $root_path_js = Cobrand::root_path_js($cobrand);
-
- # should get the results of the root_path_js function in the cobrand module if one exists
- is($root_path_js, 'root path js', 'root_path_js returns output from cobrand module');
-
- # should return a js string setting the root path to an empty string otherwise
- $cobrand = 'nosite';
- $root_path_js = Cobrand::root_path_js($cobrand);
- is($root_path_js, 'var root_path = "";', 'root_path_pattern returns a string setting the root path to an empty string if no cobrand module');
-}
-
-sub test_site_title {
- my $cobrand = 'mysite';
- my $site_title = Cobrand::site_title($cobrand);
-
- # should get the results of the site_title function in the cobrand module if one exists
- is($site_title, 'Mysite Title', 'site_title returns output from cobrand module');
-
- # should return an empty string otherwise
- $cobrand = 'nosite';
- $site_title = Cobrand::site_title($cobrand);
- is($site_title, '', 'site_title returns an empty string if no site title');
-}
-
-sub test_on_map_list_limit {
- my $cobrand = 'mysite';
- my $limit = Cobrand::on_map_list_limit($cobrand);
-
- is($limit, 30, 'on_map_list_limit returns output from cobrand module');
-
- $cobrand = 'nosite';
- $limit = Cobrand::on_map_list_limit($cobrand);
- is($limit, undef, 'on_map_list_limit returns undef if there is no limit defined by the cobrand');
-
-}
-
-sub test_url {
- my $cobrand = 'mysite';
- my $q = new MockQuery('mysite');
- my $url = Cobrand::url($cobrand, '/xyz', $q);
- is($url, '/transformed_url', 'url returns output from cobrand module');
-
- $cobrand = 'nosite';
- $url = Cobrand::url($cobrand, '/xyz', $q);
- is($url, '/xyz', 'url returns passed url if there is no url function defined by the cobrand');
-}
-
-sub test_allow_photo_upload {
- my $cobrand = 'mysite';
- my $photo_upload = Cobrand::allow_photo_upload($cobrand);
- is($photo_upload, 0, 'allow_photo_upload returns output from cobrand module');
-
- $cobrand = 'nosite';
- $photo_upload = Cobrand::allow_photo_upload($cobrand);
- is($photo_upload, 1, 'allow_photo_upload returns 1 if there is no allow_photo_upload function defined by the cobrand');
-}
-
-sub test_allow_photo_display {
- my $cobrand = 'mysite';
- my $photo_display = Cobrand::allow_photo_display($cobrand);
- is($photo_display, 0, 'allow_photo_display returns output from cobrand module');
-
- $cobrand = 'nosite';
- $photo_display = Cobrand::allow_photo_display($cobrand);
- is($photo_display, 1, 'allow_photo_display returns 1 if there is no allow_photo_display function defined by the cobrand');
-}
-
-sub test_council_check {
- my $cobrand = 'mysite';
- my $councils = {};
- my $query = new MockQuery('mysite');
- my ($check_result, $error) = Cobrand::council_check($cobrand, $councils, $query);
- is($check_result, 0, 'council_check returns output from cobrand module');
-
- $cobrand = 'nosite';
- ($check_result, $error) = Cobrand::council_check($cobrand, $councils, $query);
- is($check_result, 1, 'council_check returns 1 if there is no council_check function defined by the cobrand');
-}
-
-sub test_recent {
- my $cobrand = 'mysite';
- my $check_result = Cobrand::recent($cobrand);
- is_deeply($check_result, [ { id => 1, title => 'Title 1' }, { id => 2, title => 'Title 2' } ], 'recent returns output from cobrand module');
-
- # Can't test default here as calls database. So test it throws a db not configured error :)
- my $error;
- $cobrand = 'nosite';
- try {
- $check_result = Cobrand::recent($cobrand);
- } catch Error with {
- $error = shift;
- };
- ok($error =~ /^configure not yet called in new_dbh/, 'Default throws a database error');
-}
-
-ok(test_cobrand_handle() == 1, 'Ran all tests for the cobrand_handle function');
-ok(test_site_restriction() == 1, 'Ran all tests for the site_restriction function');
-ok(test_base_url() == 1, 'Ran all tests for the base url');
-ok(test_disambiguate_location() == 1, 'Ran all tests for disambiguate location');
-ok(test_form_elements() == 1, 'Ran all tests for form_elements');
-ok(test_base_url_for_emails() == 1, 'Ran all tests for base_url_for_emails');
-ok(test_extra_problem_data() == 1, 'Ran all tests for extra_problem_data');
-ok(test_extra_update_data() == 1, 'Ran all tests for extra_update_data');
-ok(test_extra_alert_data() == 1, 'Ran all tests for extra_alert_data');
-ok(test_extra_params() == 1, 'Ran all tests for extra_params');
-ok(test_header_params() == 1, 'Ran all tests for header_params');
-ok(test_root_path_js() == 1, 'Ran all tests for root_js');
-ok(test_site_title() == 1, 'Ran all tests for site_title');
-ok(test_on_map_list_limit() == 1, 'Ran all tests for on_map_list_limit');
-ok(test_url() == 1, 'Ran all tests for url');
-ok(test_allow_photo_upload() == 1, 'Ran all tests for allow_photo_upload');
-ok(test_allow_photo_display() == 1, 'Ran all tests for allow_photo_display');
-ok(test_council_check() == 1, 'Ran all tests for council_check');
-ok(test_recent() == 1, 'Ran all tests for recent');
diff --git a/t/Cobrands/.gitignore b/t/Cobrands/.gitignore
deleted file mode 100644
index 77650dd46..000000000
--- a/t/Cobrands/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/Cities \ No newline at end of file
diff --git a/t/Cobrands/Mysite/Util.pm b/t/Cobrands/Mysite/Util.pm
deleted file mode 100644
index c1a8023c1..000000000
--- a/t/Cobrands/Mysite/Util.pm
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Util.pm:
-# Test Cobranding for FixMyStreet.
-#
-#
-# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
-# Email: louise@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: Util.pm,v 1.20 2009-12-16 12:43:13 matthew Exp $
-
-package Cobrands::Mysite::Util;
-use Page;
-use strict;
-use Carp;
-use mySociety::Web qw(ent);
-
-sub new {
- my $class = shift;
- return bless {}, $class;
-}
-
-sub site_name {
- return 'mysite';
-}
-
-sub site_restriction {
- return (' and council = 1 ', 99);
-}
-
-sub page {
- my %params = ();
- return ("A cobrand produced page", %params);
-}
-
-sub base_url {
- return 'http://mysite.example.com';
-}
-
-sub base_url_for_emails {
- return 'http://mysite.foremails.example.com';
-}
-
-sub disambiguate_location {
- return 'Specific Location';
-}
-
-sub form_elements {
- return "Extra html";
-}
-
-sub extra_problem_data {
- return "Cobrand problem data";
-}
-
-sub extra_update_data {
- return "Cobrand update data";
-}
-
-sub extra_alert_data {
- return "Cobrand alert data";
-}
-
-sub extra_params {
- return 'key=value';
-}
-
-sub header_params {
- my %params = ('key' => 'value');
- return \%params;
-}
-
-
-sub root_path_js {
- return 'root path js';
-}
-
-sub site_title {
- return 'Mysite Title';
-}
-
-sub on_map_list_limit {
- return 30;
-}
-
-sub url {
- return '/transformed_url';
-}
-
-sub allow_photo_upload {
- return 0;
-}
-
-sub allow_photo_display {
- return 0;
-}
-
-sub council_check {
- return 0;
-}
-
-sub recent {
- return [
- { id => 1, title => 'Title 1' },
- { id => 2, title => 'Title 2' }
- ];
-}
-
-1;
diff --git a/t/Page.t b/t/Page.t
deleted file mode 100755
index a1ae7f3aa..000000000
--- a/t/Page.t
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Page.t:
-# Tests for the Page functions
-#
-# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
-# Email: louise@mysociety.org; WWW: http://www.mysociety.org/
-#
-# $Id: Page.t,v 1.12 2009-12-09 13:34:36 louise Exp $
-#
-
-use strict;
-use warnings;
-use Test::More tests => 13;
-use Test::Exception;
-
-use FindBin;
-use lib "$FindBin::Bin";
-use lib "$FindBin::Bin/../perllib";
-use lib "$FindBin::Bin/../commonlib/perllib";
-
-use Page;
-use FixMyStreet::Geocode;
-use mySociety::MockQuery;
-use mySociety::Locale;
-
-sub mock_query(){
- my $q = new MockQuery('mysite');
- return $q;
-}
-
-sub set_lang($) {
- my $lang = shift;
- mySociety::Locale::negotiate_language($lang);
- mySociety::Locale::gettext_domain('FixMyStreet');
- mySociety::Locale::change();
-}
-
-sub test_geocode_string() {
- my %params = ();
- my $q = new MockQuery( 'nosite', \%params );
-
- # geocode a straightforward string, expect success
- my ( $latitude, $longitude, $error ) =
- FixMyStreet::Geocode::string( 'Buckingham Palace', $q );
- is( $latitude, 51.5013639, 'example easting generated' );
- is( $longitude, -0.1418898, 'example northing generated' );
- is( $error, undef, 'should not generate error for simple example' );
-
- # expect a failure message for Northern Ireland
- ( $latitude, $longitude, $error ) =
- FixMyStreet::Geocode::string( 'Falls Road, Belfast', $q );
- is(
- $error,
- "We do not cover Northern Ireland, I'm afraid, as our licence doesn't "
- . "include any maps for the region.",
- 'error message produced for NI location'
- );
-}
-
-sub test_header() {
- my $q = mock_query();
- my $html;
- my %params = (title => 'test title');
- set_lang('en-gb,English,en_GB');
-
- # Test that param that isn't explicitly allowed raises error
- $params{'test-param'} = 'test';
- throws_ok { Page::header($q, %params); } qr/bad parameter/, 'bad parameter caught ok';
- delete $params{'test-param'};
-
- # Test that template passed is rendered
- $params{'template'} = 'test';
- $html = Page::template_include('test-header', $q,
- '/../t/templates/' . $q->{site} . '/',
- title => 'My test title', lang => 'en-gb'
- );
-
- like ($html, qr/My test header template/, 'named template rendered ok');
-
- return 1;
-}
-
-sub test_footer(){
- return 1;
-}
-
-sub test_base_url_with_lang {
- set_lang('en-gb,English,en_GB');
- my $q = mock_query();
- my $url = Page::base_url_with_lang($q);
- ok($url eq 'http://mysite.example.com', 'Basic url rendered ok');
-
- $q = new MockQuery('emptyhomes');
- $url = Page::base_url_with_lang($q);
- like($url, qr/http:\/\/en\.emptyhomes\./, 'Empty homes url with lang returned ok');
-
- $url = Page::base_url_with_lang($q, 1);
- like($url, qr/http:\/\/cy\.emptyhomes\./, 'Empty homes url with lang reversed returned ok');
-
-}
-
-
-ok(test_base_url_with_lang() == 1, 'Ran all tests for base_url_with_lang');
-ok(test_footer() == 1, 'Ran all tests for the footer function');
-ok(test_header() == 1, 'Ran all tests for the header function');
-ok(test_geocode_string() == 1, 'Ran all tests for the geocode_string function');
diff --git a/t/Problems.t b/t/Problems.t
deleted file mode 100755
index 40b3b810c..000000000
--- a/t/Problems.t
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Problem.t:
-# Tests for the Problem functions
-#
-# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
-# Email: louise@mysociety.org; WWW: http://www.mysociety.org/
-#
-# $Id: Problems.t,v 1.3 2009-11-12 14:05:18 louise Exp $
-#
-
-use strict;
-use warnings;
-use Test::More tests => 1;
-use Test::Exception;
-
-use FindBin;
-use lib "$FindBin::Bin";
-use lib "$FindBin::Bin/../perllib";
-use lib "$FindBin::Bin/../commonlib/perllib";
-BEGIN { use_ok( 'Problems' ); }
-
diff --git a/t/app/01app.t b/t/app/01app.t
new file mode 100644
index 000000000..02ffcd217
--- /dev/null
+++ b/t/app/01app.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+use Catalyst::Test 'FixMyStreet::App';
+
+ok( request('/')->is_success, 'Request should succeed' );
+
+done_testing();
diff --git a/t/app/02pod.t b/t/app/02pod.t
new file mode 100644
index 000000000..ababc2eaa
--- /dev/null
+++ b/t/app/02pod.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+eval "use Test::Pod 1.14";
+plan skip_all => 'Test::Pod 1.14 required' if $@;
+
+all_pod_files_ok();
diff --git a/t/app/03podcoverage.t b/t/app/03podcoverage.t
new file mode 100644
index 000000000..6ddc5c6b6
--- /dev/null
+++ b/t/app/03podcoverage.t
@@ -0,0 +1,14 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => 'Test::Pod::Coverage 1.04 required' if $@;
+
+eval "use Pod::Coverage 0.20";
+plan skip_all => 'Pod::Coverage 0.20 required' if $@;
+
+all_pod_coverage_ok();
diff --git a/t/app/controller/about.t b/t/app/controller/about.t
new file mode 100644
index 000000000..a5fb215d3
--- /dev/null
+++ b/t/app/controller/about.t
@@ -0,0 +1,31 @@
+use strict;
+use warnings;
+
+use Test::More;
+use Test::WWW::Mechanize::Catalyst 'FixMyStreet::App';
+
+ok( my $mech = Test::WWW::Mechanize::Catalyst->new, 'Created mech object' );
+
+# check that we can get the page
+$mech->get_ok('/about');
+$mech->content_like(qr{About us ::\s+FixMyStreet});
+$mech->content_contains('html lang="en-gb"');
+
+SKIP: {
+ skip( "Need 'emptyhomes' in ALLOWED_COBRANDS config", 8 )
+ unless FixMyStreet::App->config->{ALLOWED_COBRANDS} =~ m{emptyhomes};
+
+ # check that geting the page as EHA produces a different page
+ ok $mech->host("reportemptyhomes.co.uk"), 'change host to reportemptyhomes';
+ $mech->get_ok('/about');
+ $mech->content_like(qr{About us ::\s+Report Empty Homes});
+ $mech->content_contains('html lang="en-gb"');
+
+ # check that geting the page as EHA in welsh produces a different page
+ ok $mech->host("cy.reportemptyhomes.co.uk"), 'host to cy.reportemptyhomes';
+ $mech->get_ok('/about');
+ $mech->content_like(qr{Amdanom ni ::\s+Adrodd am Eiddo Gwag});
+ $mech->content_contains('html lang="cy"');
+}
+
+done_testing();
diff --git a/t/app/controller/admin.t b/t/app/controller/admin.t
new file mode 100644
index 000000000..c5416832f
--- /dev/null
+++ b/t/app/controller/admin.t
@@ -0,0 +1,652 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+
+my $mech = FixMyStreet::TestMech->new;
+
+my $secret = FixMyStreet::App->model('DB::Secret')->search();
+
+# don't explode if there's nothing in the secret table
+if ( $secret == 0 ) {
+ diag "You need to put an entry in the secret table for the admin tests to run";
+ plan skip_all => 'No entry in secret table';
+}
+
+$mech->get_ok('/admin');
+$mech->title_like(qr/Summary/);
+
+$mech->get_ok('/admin/council_contacts/2650');
+$mech->content_contains('Aberdeen City Council');
+$mech->content_contains('AB15 8RN');
+
+subtest 'check contact creation' => sub {
+ my $contact = FixMyStreet::App->model('DB::Contact')->find(
+ { area_id => 2650, category => 'test category' }
+ );
+
+ $contact->delete if $contact;
+
+ my $history = FixMyStreet::App->model('DB::ContactsHistory')->search(
+ { area_id => 2650, category => 'test category' }
+ );
+
+ $history->delete_all;
+
+ $mech->get_ok('/admin/council_contacts/2650');
+
+ $mech->submit_form_ok( { with_fields => {
+ category => 'test category',
+ email => 'test@example.com',
+ note => 'test note',
+ } } );
+
+ $mech->content_contains( 'test category' );
+ $mech->content_contains( '<td>test@example.com' );
+ $mech->content_contains( '<td>test note' );
+};
+
+subtest 'check contact editing' => sub {
+ $mech->get_ok('/admin/council_edit/2650/test%20category');
+
+ $mech->submit_form_ok( { with_fields => {
+ email => 'test2@example.com',
+ note => 'test2 note',
+ } } );
+
+ $mech->content_contains( 'test category' );
+ $mech->content_contains( '<td>test2@example.com' );
+ $mech->content_contains( '<td>test2 note' );
+
+ $mech->get_ok('/admin/council_edit/2650/test%20category');
+ $mech->content_contains( '<td><strong>test2@example.com' );
+};
+
+subtest 'check contact updating' => sub {
+ $mech->get_ok('/admin/council_edit/2650/test%20category');
+ $mech->content_like(qr{test2\@example.com</strong>[^<]*</td>[^<]*<td>No}s);
+
+ $mech->get_ok('/admin/council_contacts/2650');
+
+ $mech->form_number( 1 );
+ $mech->tick( 'confirmed', 'test category' );
+ $mech->submit_form_ok({form_number => 1});
+
+ $mech->content_like(qr'test2@example.com</td>[^<]*<td>Yes's);
+ $mech->get_ok('/admin/council_edit/2650/test%20category');
+ $mech->content_like(qr{test2\@example.com[^<]*</td>[^<]*<td><strong>Yes}s);
+};
+
+my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'test@example.com', name => 'Test User' } );
+ok $user, "created test user";
+
+my $user2 =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'test2@example.com', name => 'Test User 2' } );
+ok $user2, "created second test user";
+
+
+my $user3 =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => 'test3@example.com', name => 'Test User 2' } );
+
+if ( $user3 ) {
+ $mech->delete_user( $user3 );
+}
+
+my $dt = DateTime->new(
+ year => 2011,
+ month => 04,
+ day => 16,
+ hour => 15,
+ minute => 47,
+ second => 23
+);
+
+my $report = FixMyStreet::App->model('DB::Problem')->find_or_create(
+ {
+ postcode => 'SW1A 1AA',
+ council => '2504',
+ areas => ',105255,11806,11828,2247,2504,',
+ category => 'Other',
+ title => 'Report to Edit',
+ detail => 'Detail for Report to Edit',
+ used_map => 't',
+ name => 'Test User',
+ anonymous => 'f',
+ state => 'confirmed',
+ confirmed => $dt->ymd . ' ' . $dt->hms,
+ lang => 'en-gb',
+ service => '',
+ cobrand => 'default',
+ cobrand_data => '',
+ send_questionnaire => 't',
+ latitude => '51.5016605453401',
+ longitude => '-0.142497580865087',
+ user_id => $user->id,
+ whensent => $dt->ymd . ' ' . $dt->hms,
+ }
+);
+
+my $log_entries = FixMyStreet::App->model('DB::AdminLog')->search(
+ {
+ object_type => 'problem',
+ object_id => $report->id
+ },
+ {
+ order_by => { -desc => 'id' },
+ }
+);
+
+is $log_entries->count, 0, 'no admin log entries';
+
+my $report_id = $report->id;
+ok $report, "created test report - $report_id";
+
+foreach my $test (
+ {
+ description => 'edit report title',
+ fields => {
+ title => 'Report to Edit',
+ detail => 'Detail for Report to Edit',
+ state => 'confirmed',
+ name => 'Test User',
+ email => $user->email,
+ anonymous => 0,
+ },
+ changes => {
+ title => 'Edited Report',
+ },
+ log_count => 1,
+ log_entries => [ qw/edit/ ],
+ resend => 0,
+ },
+ {
+ description => 'edit report description',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Detail for Report to Edit',
+ state => 'confirmed',
+ name => 'Test User',
+ email => $user->email,
+ anonymous => 0,
+ },
+ changes => {
+ detail => 'Edited Detail',
+ },
+ log_count => 2,
+ log_entries => [ qw/edit edit/ ],
+ resend => 0,
+ },
+ {
+ description => 'edit report user name',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Edited Detail',
+ state => 'confirmed',
+ name => 'Test User',
+ email => $user->email,
+ anonymous => 0,
+ },
+ changes => {
+ name => 'Edited User',
+ },
+ log_count => 3,
+ log_entries => [ qw/edit edit edit/ ],
+ resend => 0,
+ user => $user,
+ },
+ {
+ description => 'edit report user email',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Edited Detail',
+ state => 'confirmed',
+ name => 'Edited User',
+ email => $user->email,
+ anonymous => 0,
+ },
+ changes => {
+ email => $user2->email,
+ },
+ log_count => 4,
+ log_entries => [ qw/edit edit edit edit/ ],
+ resend => 0,
+ user => $user2,
+ },
+ {
+ description => 'change state to unconfirmed',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Edited Detail',
+ state => 'confirmed',
+ name => 'Edited User',
+ email => $user2->email,
+ anonymous => 0,
+ },
+ changes => {
+ state => 'unconfirmed'
+ },
+ log_count => 5,
+ log_entries => [ qw/state_change edit edit edit edit/ ],
+ resend => 0,
+ },
+ {
+ description => 'change state to confirmed',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Edited Detail',
+ state => 'unconfirmed',
+ name => 'Edited User',
+ email => $user2->email,
+ anonymous => 0,
+ },
+ changes => {
+ state => 'confirmed'
+ },
+ log_count => 6,
+ log_entries => [ qw/state_change state_change edit edit edit edit/ ],
+ resend => 0,
+ },
+ {
+ description => 'change state to fixed',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Edited Detail',
+ state => 'confirmed',
+ name => 'Edited User',
+ email => $user2->email,
+ anonymous => 0,
+ },
+ changes => {
+ state => 'fixed'
+ },
+ log_count => 7,
+ log_entries => [ qw/state_change state_change state_change edit edit edit edit/ ],
+ resend => 0,
+ },
+ {
+ description => 'change state to hidden',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Edited Detail',
+ state => 'fixed',
+ name => 'Edited User',
+ email => $user2->email,
+ anonymous => 0,
+ },
+ changes => {
+ state => 'hidden'
+ },
+ log_count => 8,
+ log_entries => [ qw/state_change state_change state_change state_change edit edit edit edit/ ],
+ resend => 0,
+ },
+ {
+ description => 'edit and change state',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Edited Detail',
+ state => 'hidden',
+ name => 'Edited User',
+ email => $user2->email,
+ anonymous => 0,
+ },
+ changes => {
+ state => 'confirmed',
+ anonymous => 1,
+ },
+ log_count => 10,
+ log_entries => [ qw/edit state_change state_change state_change state_change state_change edit edit edit edit/ ],
+ resend => 0,
+ },
+ {
+ description => 'resend',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Edited Detail',
+ state => 'confirmed',
+ name => 'Edited User',
+ email => $user2->email,
+ anonymous => 1,
+ },
+ changes => {
+ },
+ log_count => 11,
+ log_entries => [ qw/resend edit state_change state_change state_change state_change state_change edit edit edit edit/ ],
+ resend => 1,
+ },
+) {
+ subtest $test->{description} => sub {
+ $log_entries->reset;
+ $mech->get_ok("/admin/report_edit/$report_id");
+
+ is_deeply( $mech->visible_form_values(), $test->{fields}, 'initial form values' );
+
+ my $new_fields = {
+ %{ $test->{fields} },
+ %{ $test->{changes} },
+ };
+
+ if ( $test->{resend} ) {
+ $mech->click_ok( 'resend' );
+ } else {
+ $mech->submit_form_ok( { with_fields => $new_fields }, 'form_submitted' );
+ }
+
+ is_deeply( $mech->visible_form_values(), $new_fields, 'changed form values' );
+ is $log_entries->count, $test->{log_count}, 'log entry count';
+ is $log_entries->next->action, $_, 'log entry added' for @{ $test->{log_entries} };
+
+ $report->discard_changes;
+
+ if ( $report->state eq 'confirmed' ) {
+ $mech->content_contains( 'type="submit" name="resend"', 'no resend button' );
+ } else {
+ $mech->content_lacks( 'type="submit" name="resend"', 'no resend button' );
+ }
+
+ is $report->$_, $test->{changes}->{$_}, "$_ updated" for grep { $_ ne 'email' } keys %{ $test->{changes} };
+
+ if ( $test->{user} ) {
+ is $report->user->id, $test->{user}->id, 'user changed';
+ }
+
+ if ( $test->{resend} ) {
+ $mech->content_contains( 'That problem will now be resent' );
+ is $report->whensent, undef, 'mark report to resend';
+ }
+ };
+}
+
+subtest 'change email to new user' => sub {
+ $log_entries->delete;
+ $mech->get_ok("/admin/report_edit/$report_id");
+ my $fields = {
+ title => $report->title,
+ detail => $report->detail,
+ state => $report->state,
+ name => $report->name,
+ email => $report->user->email,
+ anonymous => 1,
+ };
+
+ is_deeply( $mech->visible_form_values(), $fields, 'initial form values' );
+
+ my $changes = {
+ email => 'test3@example.com'
+ };
+
+ $user3 =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => 'test3@example.com', name => 'Test User 2' } );
+
+ ok !$user3, 'user not in database';
+
+ my $new_fields = {
+ %{ $fields },
+ %{ $changes },
+ };
+
+ $mech->submit_form_ok(
+ {
+ with_fields => $new_fields,
+ }
+ );
+
+ is $log_entries->count, 1, 'created admin log entries';
+ is $log_entries->first->action, 'edit', 'log action';
+ is_deeply( $mech->visible_form_values(), $new_fields, 'changed form values' );
+
+ $user3 =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => 'test3@example.com', name => 'Test User 2' } );
+
+ $report->discard_changes;
+
+ ok $user3, 'new user created';
+ is $report->user_id, $user3->id, 'user changed to new user';
+};
+
+$log_entries->delete;
+
+my $update = FixMyStreet::App->model('DB::Comment')->create(
+ {
+ text => 'this is an update',
+ user => $user,
+ state => 'confirmed',
+ problem => $report,
+ mark_fixed => 0,
+ anonymous => 1,
+ }
+);
+
+$log_entries = FixMyStreet::App->model('DB::AdminLog')->search(
+ {
+ object_type => 'update',
+ object_id => $update->id
+ },
+ {
+ order_by => { -desc => 'id' },
+ }
+);
+
+is $log_entries->count, 0, 'no admin log entries';
+
+for my $test (
+ {
+ desc => 'edit update text',
+ fields => {
+ text => 'this is an update',
+ state => 'confirmed',
+ name => '',
+ anonymous => 1,
+ email => 'test@example.com',
+ },
+ changes => {
+ text => 'this is a changed update',
+ },
+ log_count => 1,
+ log_entries => [qw/edit/],
+ },
+ {
+ desc => 'edit update name',
+ fields => {
+ text => 'this is a changed update',
+ state => 'confirmed',
+ name => '',
+ anonymous => 1,
+ email => 'test@example.com',
+ },
+ changes => {
+ name => 'A User',
+ },
+ log_count => 2,
+ log_entries => [qw/edit edit/],
+ },
+ {
+ desc => 'edit update anonymous',
+ fields => {
+ text => 'this is a changed update',
+ state => 'confirmed',
+ name => 'A User',
+ anonymous => 1,
+ email => 'test@example.com',
+ },
+ changes => {
+ anonymous => 0,
+ },
+ log_count => 3,
+ log_entries => [qw/edit edit edit/],
+ },
+ {
+ desc => 'edit update user',
+ fields => {
+ text => 'this is a changed update',
+ state => 'confirmed',
+ name => 'A User',
+ anonymous => 0,
+ email => $update->user->email,
+ email => 'test@example.com',
+ },
+ changes => {
+ email => 'test2@example.com',
+ },
+ log_count => 4,
+ log_entries => [qw/edit edit edit edit/],
+ user => $user2,
+ },
+ {
+ desc => 'edit update state',
+ fields => {
+ text => 'this is a changed update',
+ state => 'confirmed',
+ name => 'A User',
+ anonymous => 0,
+ email => 'test2@example.com',
+ },
+ changes => {
+ state => 'unconfirmed',
+ },
+ log_count => 5,
+ log_entries => [qw/state_change edit edit edit edit/],
+ },
+ {
+ desc => 'edit update state and text',
+ fields => {
+ text => 'this is a changed update',
+ state => 'unconfirmed',
+ name => 'A User',
+ anonymous => 0,
+ email => 'test2@example.com',
+ },
+ changes => {
+ text => 'this is a twice changed update',
+ state => 'hidden',
+ },
+ log_count => 7,
+ log_entries => [qw/edit state_change state_change edit edit edit edit/],
+ },
+) {
+ subtest $test->{desc} => sub {
+ $log_entries->reset;
+ $mech->get_ok('/admin/update_edit/' . $update->id );
+
+ is_deeply $mech->visible_form_values, $test->{fields}, 'initial form values';
+
+ my $to_submit = {
+ %{ $test->{fields} },
+ %{ $test->{changes} }
+ };
+
+ $mech->submit_form_ok( { with_fields => $to_submit } );
+
+ is_deeply $mech->visible_form_values, $to_submit, 'submitted form values';
+
+ is $log_entries->count, $test->{log_count}, 'number of log entries';
+ is $log_entries->next->action, $_, 'log action' for @{ $test->{log_entries} };
+
+ $update->discard_changes;
+
+ is $update->$_, $test->{changes}->{$_} for grep { $_ ne 'email' } keys %{ $test->{changes} };
+
+ if ( $test->{user} ) {
+ is $update->user->id, $test->{user}->id, 'update user';
+ }
+ };
+}
+
+subtest 'editing update email creates new user if required' => sub {
+ my $user = FixMyStreet::App->model('DB::User')->find(
+ { email => 'test4@example.com' }
+ );
+
+ $user->delete if $user;
+
+ my $fields = {
+ text => 'this is a changed update',
+ state => 'hidden',
+ name => 'A User',
+ anonymous => 0,
+ email => 'test4@example.com',
+ };
+
+ $mech->submit_form_ok( { with_fields => $fields } );
+
+ $user = FixMyStreet::App->model('DB::User')->find(
+ { email => 'test4@example.com' }
+ );
+
+ is_deeply $mech->visible_form_values, $fields, 'submitted form values';
+
+ ok $user, 'new user created';
+
+ $update->discard_changes;
+ is $update->user->id, $user->id, 'update set to new user';
+};
+
+subtest 'hiding comment marked as fixed reopens report' => sub {
+ $update->mark_fixed( 1 );
+ $update->update;
+
+ $report->state('fixed');
+ $report->update;
+
+
+ my $fields = {
+ text => 'this is a changed update',
+ state => 'hidden',
+ name => 'A User',
+ anonymous => 0,
+ email => 'test2@example.com',
+ };
+
+ $mech->submit_form_ok( { with_fields => $fields } );
+
+ $report->discard_changes;
+ is $report->state, 'confirmed', 'report reopened';
+ $mech->content_contains('Problem marked as open');
+};
+
+$log_entries->delete;
+
+subtest 'report search' => sub {
+ $update->state('confirmed');
+ $update->user($report->user);
+ $update->update;
+
+ $mech->get_ok('/admin/search_reports');
+ $mech->get_ok('/admin/search_reports?search=' . $report->id );
+
+ $mech->content_contains( $report->title );
+ my $r_id = $report->id;
+ $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id/">$r_id</a>} );
+
+ $mech->get_ok('/admin/search_reports?search=' . $report->user->email);
+
+ my $u_id = $update->id;
+ $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id/">$r_id</a>} );
+ $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id/#update_$u_id">$u_id</a>} );
+
+ $update->state('hidden');
+ $update->update;
+
+ $mech->get_ok('/admin/search_reports?search=' . $report->user->email);
+ $mech->content_like( qr{<tr [^>]*hidden[^>]*> \s* <td> \s* $u_id \s* </td>}xs );
+
+ $report->state('hidden');
+ $report->update;
+
+ $mech->get_ok('/admin/search_reports?search=' . $report->user->email);
+ $mech->content_like( qr{<tr [^>]*hidden[^>]*> \s* <td> \s* $r_id \s* </td>}xs );
+};
+
+$mech->delete_user( $user );
+$mech->delete_user( $user2 );
+$mech->delete_user( $user3 );
+$mech->delete_user( 'test4@example.com' );
+
+done_testing();
diff --git a/t/app/controller/alert.t b/t/app/controller/alert.t
new file mode 100644
index 000000000..b8e38ec92
--- /dev/null
+++ b/t/app/controller/alert.t
@@ -0,0 +1,66 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'FixMyStreet::App';
+use Test::WWW::Mechanize::Catalyst 'FixMyStreet::App';
+
+ok( my $mech = Test::WWW::Mechanize::Catalyst->new, 'Created mech object' );
+
+# check that we can get the page
+$mech->get_ok('/alert');
+$mech->title_like(qr/^Local RSS feeds and email alerts/);
+$mech->content_contains('Local RSS feeds and email alerts');
+$mech->content_contains('html lang="en-gb"');
+
+# check that we can get list page
+$mech->get_ok('/alert/list');
+$mech->title_like(qr/^Local RSS feeds and email alerts/);
+$mech->content_contains('Local RSS feeds and email alerts');
+$mech->content_contains('html lang="en-gb"');
+
+$mech->get_ok('/alert/list?pc=EH99 1SP');
+$mech->title_like(qr/^Local RSS feeds and email alerts/);
+$mech->content_contains('Here are the types of local problem alerts for &lsquo;EH99&nbsp;1SP&rsquo;');
+$mech->content_contains('html lang="en-gb"');
+$mech->content_contains('Problems within 8.5km');
+$mech->content_contains('rss/pc/EH991SP/2');
+$mech->content_contains('rss/pc/EH991SP/5');
+$mech->content_contains('rss/pc/EH991SP/10');
+$mech->content_contains('rss/pc/EH991SP/20');
+$mech->content_contains('Problems within City of Edinburgh');
+$mech->content_contains('Problems within City Centre ward');
+$mech->content_contains('/rss/reports/City+of+Edinburgh');
+$mech->content_contains('/rss/reports/City+of+Edinburgh/City+Centre');
+$mech->content_contains('council:2651:City_of_Edinburgh');
+$mech->content_contains('ward:2651:20728:City_of_Edinburgh:City_Centre');
+
+$mech->get_ok('/alert/list?pc=High Street');
+$mech->content_contains('We found more than one match for that location');
+
+$mech->get_ok('/alert/list?pc=');
+$mech->content_contains('hat location does not appear to be covered by a council');
+
+$mech->get_ok('/alert/list?pc=GL502PR');
+$mech->content_contains('Problems within the boundary of');
+
+$mech->get_ok('/alert/subscribe?rss=1&type=local&pc=ky16+8yg&rss=Give+me+an+RSS+feed&rznvy=' );
+$mech->content_contains('Please select the feed you want');
+
+$mech->get_ok('/alert/subscribe?rss=1&feed=invalid:1000:A_Locationtype=local&pc=ky16+8yg&rss=Give+me+an+RSS+feed&rznvy=');
+$mech->content_contains('Illegal feed selection');
+
+$mech->get_ok('/alert/subscribe?rss=1&feed=area:1000:Birmingham');
+is $mech->uri->path, '/rss/reports/Birmingham';
+
+$mech->get_ok('/alert/subscribe?rss=1&feed=area:1000:1001:Cheltenham:Lansdown');
+is $mech->uri->path, '/rss/area/Cheltenham/Lansdown';
+
+$mech->get_ok('/alert/subscribe?rss=1&feed=council:1000:Gloucestershire');
+is $mech->uri->path, '/rss/reports/Gloucestershire';
+
+$mech->get_ok('/alert/subscribe?rss=1&feed=ward:1000:1001:Cheltenham:Lansdown');
+is $mech->uri->path, '/rss/reports/Cheltenham/Lansdown';
+
+done_testing();
diff --git a/t/app/controller/alert_new.t b/t/app/controller/alert_new.t
new file mode 100644
index 000000000..5bf7e31dd
--- /dev/null
+++ b/t/app/controller/alert_new.t
@@ -0,0 +1,486 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+
+my $mech = FixMyStreet::TestMech->new;
+
+foreach my $test (
+ {
+ email => 'test@example.com',
+ type => 'area_problems',
+ content => 'your alert will not be activated',
+ email_text => 'confirm the alert',
+ uri =>
+'/alert/subscribe?type=local&rznvy=test@example.com&feed=area:1000:A_Location',
+ param1 => 1000
+ },
+ {
+ email => 'test@example.com',
+ type => 'council_problems',
+ content => 'your alert will not be activated',
+ email_text => 'confirm the alert',
+ uri =>
+'/alert/subscribe?type=local&rznvy=test@example.com&feed=council:1000:A_Location',
+ param1 => 1000,
+ param2 => 1000,
+ },
+ {
+ email => 'test@example.com',
+ type => 'ward_problems',
+ content => 'your alert will not be activated',
+ email_text => 'confirm the alert',
+ uri =>
+'/alert/subscribe?type=local&rznvy=test@example.com&feed=ward:1000:1001:A_Location:Diff_Location',
+ param1 => 1000,
+ param2 => 1001,
+ },
+ {
+ email => 'test@example.com',
+ type => 'local_problems',
+ content => 'your alert will not be activated',
+ email_text => 'confirm the alert',
+ uri =>
+'/alert/subscribe?type=local&rznvy=test@example.com&feed=local:10.2:20.1',
+ param1 => 20.1,
+ param2 => 10.2,
+ },
+ {
+ email => 'test@example.com',
+ type => 'new_updates',
+ content => 'your alert will not be activated',
+ email_text => 'confirm the alert',
+ uri => '/alert/subscribe?type=updates&rznvy=test@example.com&id=1',
+ param1 => 1,
+ }
+ )
+{
+ subtest "$test->{type} alert correctly created" => sub {
+ $mech->clear_emails_ok;
+
+ my $type = $test->{type};
+
+ my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => $test->{email} } );
+
+ # we don't want an alert
+ if ($user) {
+ $mech->delete_user($user);
+ }
+
+ $mech->get_ok( $test->{uri} );
+ $mech->content_contains( $test->{content} );
+
+ $user =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => $test->{email} } );
+
+ ok $user, 'user created for alert';
+
+ my $alert = FixMyStreet::App->model('DB::Alert')->find(
+ {
+ user => $user,
+ alert_type => $type,
+ parameter => $test->{param1},
+ parameter2 => $test->{param2},
+ confirmed => 0,
+ }
+ );
+
+ ok $alert, "Found the alert";
+
+ my $email = $mech->get_email;
+ ok $email, "got an email";
+ like $email->body, qr/$test->{email_text}/i, "Correct email text";
+
+ my ( $url, $url_token ) = $email->body =~ m{(http://\S+/A/)(\S+)};
+ ok $url, "extracted confirm url '$url'";
+
+ my $token = FixMyStreet::App->model('DB::Token')->find(
+ {
+ token => $url_token,
+ scope => 'alert'
+ }
+ );
+ ok $token, 'Token found in database';
+ ok $alert->id == $token->data->{id}, 'token alertid matches alert id';
+
+ $mech->clear_emails_ok;
+
+ my $existing_id = $alert->id;
+ my $existing_token = $url_token;
+
+ $mech->get_ok( $test->{uri} );
+
+ $email = $mech->get_email;
+ ok $email, 'got a second email';
+
+ ($url_token) = $email->body =~ m{http://\S+/A/(\S+)};
+ ok $url_token ne $existing_token, 'sent out a new token';
+
+ $token = FixMyStreet::App->model('DB::Token')->find(
+ {
+ token => $url_token,
+ scope => 'alert'
+ }
+ );
+
+ ok $token, 'new token found in database';
+ ok $token->data->{id} == $existing_id, 'subscribed to existing alert';
+
+ $mech->get_ok("/A/$url_token");
+ $mech->content_contains('successfully confirmed');
+
+ $alert =
+ FixMyStreet::App->model('DB::Alert')->find( { id => $existing_id, } );
+
+ ok $alert->confirmed, 'alert set to confirmed';
+ $mech->delete_user($user);
+ };
+}
+
+foreach my $test (
+ {
+ email => 'test-new@example.com',
+ type => 'area',
+ content => 'your alert will not be activated',
+ email_text => 'confirm the alert',
+ uri =>
+'/alert/subscribe?type=local&rznvy=test-new@example.com&feed=area:1000:A_Location',
+ param1 => 1000
+ }
+ )
+{
+ subtest "use existing unlogged in user in a alert" => sub {
+ $mech->log_out_ok();
+
+ my $type = $test->{type} . '_problems';
+
+ my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => $test->{email} } );
+
+ my $alert = FixMyStreet::App->model('DB::Alert')->find(
+ {
+ user => $user,
+ alert_type => $type
+ }
+ );
+ # clear existing data so we can be sure we're creating it
+ ok $alert->delete() if $alert;
+
+ $mech->get_ok( $test->{uri} );
+
+ $alert = FixMyStreet::App->model('DB::Alert')->find(
+ {
+ user => $user,
+ alert_type => $type,
+ parameter => $test->{param1},
+ parameter2 => $test->{param2},
+ confirmed => 0,
+ }
+ );
+
+ $mech->content_contains( 'Now check your email' );
+
+ ok $alert, 'New alert created with existing user';
+ $mech->delete_user($user);
+ };
+}
+
+foreach my $test (
+ {
+ desc => 'logged in user signing up',
+ user => 'test-login@example.com',
+ email => 'test-login@example.com',
+ type => 'council',
+ param1 => 2651,
+ param2 => 2651,
+ confirmed => 1,
+ },
+ {
+ desc => 'logged in user signing up with different email',
+ user => 'loggedin@example.com',
+ email => 'test-login@example.com',
+ type => 'council',
+ param1 => 2651,
+ param2 => 2651,
+ confirmed => 0,
+ delete => 1,
+ }
+ )
+{
+ subtest $test->{desc} => sub {
+ my $type = $test->{type} . '_problems';
+
+ my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => $test->{user} } );
+
+ my $alert_user =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => $test->{email} } );
+
+ $mech->log_in_ok( $test->{user} );
+
+ $mech->clear_emails_ok;
+
+ my $alert;
+ if ($alert_user) {
+ $alert = FixMyStreet::App->model('DB::Alert')->find(
+ {
+ user => $alert_user,
+ alert_type => $type
+ }
+ );
+
+ # clear existing data so we can be sure we're creating it
+ $alert->delete() if $alert;
+ }
+
+ $mech->get_ok('/alert/list?pc=EH991SP');
+
+ my $form_values = $mech->visible_form_values();
+ ok $form_values->{rznvy} eq $test->{user},
+ 'auto filled in correct email';
+
+ $mech->set_visible( [ radio => 'council:2651:City_of_Edinburgh' ],
+ [ text => $test->{email} ] );
+ $mech->click('alert');
+
+ $alert = FixMyStreet::App->model('DB::Alert')->find(
+ {
+ user => $alert_user,
+ alert_type => $type,
+ parameter => $test->{param1},
+ parameter2 => $test->{param2},
+ confirmed => $test->{confirmed},
+ }
+ );
+
+ ok $alert, 'New alert created with logged in user';
+
+ $mech->email_count_is( $test->{confirmed} ? 0 : 1 );
+
+ if ( $test->{delete} ) {
+ $mech->delete_user($user);
+ $mech->delete_user($alert_user);
+ }
+ };
+}
+
+for my $test (
+ {
+ email => 'test@example.com',
+ type => 'new_updates',
+ content => 'your alert will not be activated',
+ email_text => 'confirm the alert',
+ uri => '/alert/subscribe?type=updates&rznvy=test@example.com&id=1',
+ param1 => 1,
+ }
+ )
+{
+ subtest "cannot sign up for alert if in abuse table" => sub {
+ $mech->clear_emails_ok;
+
+ my $type = $test->{type};
+
+ my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => $test->{email} } );
+
+ # we don't want an alert
+ my $alert;
+ if ($user) {
+ $mech->delete_user($user);
+ }
+
+ my $abuse =
+ FixMyStreet::App->model('DB::Abuse')
+ ->find_or_create( { email => $test->{email} } );
+
+ $mech->get_ok( $test->{uri} );
+ $mech->content_contains( $test->{content} );
+
+ $user =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => $test->{email} } );
+
+ ok $user, 'user created for alert';
+
+ $alert = FixMyStreet::App->model('DB::Alert')->find(
+ {
+ user => $user,
+ alert_type => $type,
+ parameter => $test->{param1},
+ parameter2 => $test->{param2},
+ confirmed => 0,
+ }
+ );
+
+ ok $alert, "Found the alert";
+
+ my $email = $mech->get_email;
+ ok $email, "got an email";
+ like $email->body, qr/$test->{email_text}/i, "Correct email text";
+
+ my ( $url, $url_token ) = $email->body =~ m{(http://\S+/A/)(\S+)};
+ ok $url, "extracted confirm url '$url'";
+
+ my $token = FixMyStreet::App->model('DB::Token')->find(
+ {
+ token => $url_token,
+ scope => 'alert'
+ }
+ );
+ ok $token, 'Token found in database';
+ ok $alert->id == $token->data->{id}, 'token alertid matches alert id';
+
+ $mech->clear_emails_ok;
+
+ $mech->get_ok("/A/$url_token");
+ $mech->content_contains('error confirming');
+
+ $alert->discard_changes;
+
+ ok !$alert->confirmed, 'alert not set to confirmed';
+
+ $abuse->delete;
+ $mech->delete_user($user);
+ };
+}
+
+subtest "Test two-tier council alerts" => sub {
+ for my $alert (
+ { feed => "local:51.896269:-2.093063", result => '/rss/l/51.896269,-2.093063' },
+ { feed => "area:2326:Cheltenham", result => '/rss/area/Cheltenham' },
+ { feed => "area:2326:4544:Cheltenham:Lansdown", result => '/rss/area/Cheltenham/Lansdown' },
+ { feed => "area:2226:Gloucestershire", result => '/rss/area/Gloucestershire' },
+ { feed => "area:2226:14949:Gloucestershire:Lansdown%2C_Park_and_Warden_Hill",
+ result => '/rss/area/Gloucestershire/Lansdown%2C+Park+and+Warden+Hill'
+ },
+ { feed => "council:2326:Cheltenham", result => '/rss/reports/Cheltenham' },
+ { feed => "ward:2326:4544:Cheltenham:Lansdown", result => '/rss/reports/Cheltenham/Lansdown' },
+ { feed => "council:2226:Gloucestershire", result => '/rss/reports/Gloucestershire' },
+ { feed => "ward:2226:14949:Gloucestershire:Lansdown%2C_Park_and_Warden_Hill",
+ result => '/rss/reports/Gloucestershire/Lansdown%2C+Park+and+Warden+Hill'
+ },
+ ) {
+ $mech->get_ok( '/alert/list?pc=GL502PR' );
+ $mech->submit_form_ok( {
+ button => 'rss',
+ with_fields => {
+ feed => $alert->{feed},
+ }
+ } );
+ is $mech->uri->path, $alert->{result};
+ }
+};
+
+subtest "Test normal alert signups and that alerts are sent" => sub {
+ my $user1 = FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'reporter@example.com', name => 'Reporter User' } );
+ ok $user1, "created test user";
+ $user1->alerts->delete;
+
+ my $user2 = FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'alerts@example.com', name => 'Alert User' } );
+ ok $user2, "created test user";
+ $user2->alerts->delete;
+
+ for my $alert (
+ { feed => 'local:55.951963:-3.189944', email_confirm => 1 },
+ { feed => 'council:2651:City_of_Edinburgh', },
+ ) {
+ $mech->get_ok( '/alert' );
+ $mech->submit_form_ok( { with_fields => { pc => 'EH11BB' } } );
+ $mech->submit_form_ok( {
+ button => 'alert',
+ with_fields => {
+ rznvy => $user2->email,
+ feed => $alert->{feed},
+ }
+ } );
+ if ( $alert->{email_confirm} ) {
+ my $email = $mech->get_email;
+ $mech->clear_emails_ok;
+ my ( $url, $url_token ) = $email->body =~ m{http://\S+(/A/(\S+))};
+ my $token = FixMyStreet::App->model('DB::Token')->find( { token => $url_token, scope => 'alert' } );
+ $mech->get_ok( $url );
+ $mech->content_contains('successfully confirmed');
+ } else {
+ $mech->content_contains('successfully created');
+ }
+ }
+
+ my $dt = DateTime->now()->add( days => 2);
+
+ my $report_time = '2011-03-01 12:00:00';
+ my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( {
+ postcode => 'EH1 1BB',
+ council => '2651',
+ areas => ',11808,135007,14419,134935,2651,20728,',
+ category => 'Street lighting',
+ title => 'Testing',
+ detail => 'Testing Detail',
+ used_map => 1,
+ name => $user1->name,
+ anonymous => 0,
+ state => 'confirmed',
+ confirmed => $dt,
+ lastupdate => $dt,
+ whensent => $dt->clone->add( minutes => 5 ),
+ lang => 'en-gb',
+ service => '',
+ cobrand => 'default',
+ cobrand_data => '',
+ send_questionnaire => 1,
+ latitude => '55.951963',
+ longitude => '-3.189944',
+ user_id => $user1->id,
+ } );
+ my $report_id = $report->id;
+ ok $report, "created test report - $report_id";
+
+ my $alert = FixMyStreet::App->model('DB::Alert')->create( {
+ parameter => $report_id,
+ alert_type => 'new_updates',
+ user => $user1,
+ } )->confirm;
+ ok $alert, 'created alert for reporter';
+
+ my $update = FixMyStreet::App->model('DB::Comment')->create( {
+ problem_id => $report_id,
+ user_id => $user2->id,
+ name => 'Other User',
+ mark_fixed => 'false',
+ text => 'This is some update text',
+ state => 'confirmed',
+ confirmed => $dt->clone->add( hours => 7 ),
+ anonymous => 'f',
+ } );
+ my $update_id = $update->id;
+ ok $update, "created test update - $update_id";
+
+ FixMyStreet::App->model('DB::AlertType')->email_alerts();
+ $mech->email_count_is(3);
+ my @emails = $mech->get_email;
+ my $count;
+ for (@emails) {
+ $count++ if $_->body =~ /The following updates have been left on this problem:/;
+ $count++ if $_->body =~ /The following new problems have been reported to City of\s*Edinburgh Council:/;
+ $count++ if $_->body =~ /The following nearby problems have been added:/;
+ }
+ is $count, 3, 'Three emails with the right things in them';
+
+ my ( $url, $url_token ) = $emails[0]->body =~ m{http://\S+(/A/(\S+))};
+ $mech->get_ok( $url );
+ $mech->content_contains('successfully deleted');
+
+ $mech->delete_user($user1);
+ $mech->delete_user($user2);
+};
+
+done_testing();
diff --git a/t/app/controller/around.t b/t/app/controller/around.t
new file mode 100644
index 000000000..33c959b48
--- /dev/null
+++ b/t/app/controller/around.t
@@ -0,0 +1,106 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+my $mech = FixMyStreet::TestMech->new;
+
+subtest "check that if no query we get sent back to the homepage" => sub {
+ $mech->get_ok('/around');
+ is $mech->uri->path, '/around', "still at '/around'";
+};
+
+# x,y requests were generated by the old map code. We keep the behavior for
+# historic links
+subtest "redirect x,y requests to lat/lon (301 - permanent)" => sub {
+
+ $mech->get_ok('/around?x=3281&y=1113');
+
+ # did we redirect to lat,lon?
+ is $mech->uri->path, '/around', "still on /around";
+ is_deeply { $mech->uri->query_form },
+ { lat => 51.4998246332569, lon => -0.140137309739907, },
+ "lat,lon correctly set";
+
+ # was it a 301?
+ is $mech->res->code, 200, "got 200 for final destination";
+ is $mech->res->previous->code, 301, "got 301 for redirect";
+
+};
+
+# test various locations on inital search box
+foreach my $test (
+ {
+ pc => '', #
+ errors => [],
+ pc_alternatives => [],
+ },
+ {
+ pc => 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ errors => ['Sorry, we could not find that location.'],
+ pc_alternatives => [],
+ },
+ {
+ pc => 'glenthorpe',
+ errors => [],
+ pc_alternatives => [
+ 'Glenthorpe Crescent, Leeds LS9 7',
+ 'Glenthorpe Rd, Merton, Greater London SM4 4',
+ 'Glenthorpe Ave, Leeds LS9 7',
+ 'Glenthorne Rd, Hammersmith, Greater London W6 0',
+ 'Glenthorne Ave, Yeovil, Somerset BA21 4',
+ 'Glenthorne Rd, Kenwyn, Cornwall TR3 6',
+ 'Glenthorne Dr, Cheslyn Hay, Staffordshire WS6 7',
+ 'Glenthorne Gardens, Ilford, Greater London IG5 0',
+ 'Glenthorne Ave, Croydon, Greater London CR0 7',
+ ],
+ },
+ {
+ pc => 'Glenthorpe Ct, Katy, TX 77494, USA',
+ errors =>
+ ['Sorry, we could not find that location.'],
+ pc_alternatives => [],
+ },
+ )
+{
+ subtest "test bad pc value '$test->{pc}'" => sub {
+ $mech->get_ok('/');
+ $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } },
+ "bad location" );
+ is_deeply $mech->page_errors, $test->{errors},
+ "expected errors for pc '$test->{pc}'";
+ is_deeply $mech->pc_alternatives, $test->{pc_alternatives},
+ "expected alternatives for pc '$test->{pc}'";
+ };
+}
+
+# check that exact queries result in the correct lat,lng
+foreach my $test (
+ {
+ pc => 'SW1A 1AA',
+ latitude => '51.50101',
+ longitude => '-0.141587',
+ },
+ {
+ pc => 'Manchester',
+ latitude => '53.480713',
+ longitude => '-2.234376',
+ },
+ {
+ pc => 'Glenthorpe Rd, Merton, Greater London SM4 4, UK',
+ latitude => '51.3938',
+ longitude => '-0.22096',
+ },
+ )
+{
+ subtest "check lat/lng for '$test->{pc}'" => sub {
+ $mech->get_ok('/');
+ $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } },
+ "good location" );
+ is_deeply $mech->form_errors, [], "no errors for pc '$test->{pc}'";
+ is_deeply $mech->extract_location, $test,
+ "got expected location for pc '$test->{pc}'";
+ };
+}
+
+done_testing();
diff --git a/t/app/controller/auth.t b/t/app/controller/auth.t
new file mode 100644
index 000000000..a44716a1e
--- /dev/null
+++ b/t/app/controller/auth.t
@@ -0,0 +1,227 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+use FixMyStreet::TestMech;
+my $mech = FixMyStreet::TestMech->new;
+
+my $test_email = 'test@example.com';
+my $test_password = 'foobar';
+$mech->delete_user($test_email);
+
+END {
+ $mech->delete_user($test_email);
+ done_testing();
+}
+
+$mech->get_ok('/auth');
+
+# check that we can't reach a page that is only available to authenticated users
+$mech->not_logged_in_ok;
+
+# check that submitting form with no / bad email creates an error.
+$mech->get_ok('/auth');
+
+for my $test (
+ [ '' => 'enter your email' ],
+ [ 'not an email' => 'check your email address is correct' ],
+ [ 'bob@foo' => 'check your email address is correct' ],
+ [ 'bob@foonaoedudnueu.co.uk' => 'check your email address is correct' ],
+ )
+{
+ my ( $email, $error_message ) = @$test;
+ pass "--- testing bad email '$email' gives error '$error_message'";
+ $mech->get_ok('/auth');
+ $mech->content_lacks($error_message);
+ $mech->submit_form_ok(
+ {
+ form_name => 'general_auth',
+ fields => { email => $email, },
+ button => 'email_login',
+ },
+ "try to create an account with email '$email'"
+ );
+ is $mech->uri->path, '/auth', "still on auth page";
+ $mech->content_contains($error_message);
+}
+
+# create a new account
+$mech->clear_emails_ok;
+$mech->get_ok('/auth');
+$mech->submit_form_ok(
+ {
+ form_name => 'general_auth',
+ fields => { email => $test_email, },
+ button => 'email_login',
+ },
+ "create an account for '$test_email'"
+);
+is $mech->uri->path, '/auth/token', "redirected to welcome page";
+
+# check that we are not logged in yet
+$mech->not_logged_in_ok;
+
+# check that we got one email
+{
+ $mech->email_count_is(1);
+ my $email = $mech->get_email;
+ $mech->clear_emails_ok;
+ is $email->header('Subject'), "Your FixMyStreet.com account details",
+ "subject is correct";
+ is $email->header('To'), $test_email, "to is correct";
+
+ # extract the link
+ my ($link) = $email->body =~ m{(http://\S+)};
+ ok $link, "Found a link in email '$link'";
+
+ # check that the user does not exist
+ sub get_user {
+ FixMyStreet::App->model('DB::User')->find( { email => $test_email } );
+ }
+ ok !get_user(), "no user exists";
+
+ # visit the confirm link (with bad token) and check user no confirmed
+ $mech->get_ok( $link . 'XXX' );
+ ok !get_user(), "no user exists";
+ $mech->not_logged_in_ok;
+
+ # visit the confirm link and check user is confirmed
+ $mech->get_ok($link);
+ ok get_user(), "user created";
+ is $mech->uri->path, '/my', "redirected to the 'my' section of site";
+ $mech->logged_in_ok;
+
+ # logout and try to use the token again
+ $mech->log_out_ok;
+ $mech->get_ok($link);
+ is $mech->uri, $link, "not logged in";
+ $mech->content_contains( 'Link too old or already used',
+ 'token now invalid' );
+ $mech->not_logged_in_ok;
+}
+
+# get a login email and change password
+{
+ $mech->clear_emails_ok;
+ $mech->get_ok('/auth');
+ $mech->submit_form_ok(
+ {
+ form_name => 'general_auth',
+ fields => { email => "$test_email", },
+ button => 'email_login',
+ },
+ "email_login with '$test_email'"
+ );
+ is $mech->uri->path, '/auth/token', "redirected to token page";
+
+ # rest is as before so no need to test
+
+ # follow link and change password - check not prompted for old password
+ $mech->not_logged_in_ok;
+
+ $mech->email_count_is(1);
+ my $email = $mech->get_email;
+ $mech->clear_emails_ok;
+ my ($link) = $email->body =~ m{(http://\S+)};
+ $mech->get_ok($link);
+
+ $mech->follow_link_ok( { url => '/auth/change_password' } );
+
+ ok my $form = $mech->form_name('change_password'),
+ "found change password form";
+ is_deeply [ sort grep { $_ } map { $_->name } $form->inputs ], #
+ [ 'confirm', 'new_password' ],
+ "check we got expected fields (ie not old_password)";
+
+ # check the various ways the form can be wrong
+ for my $test (
+ { new => '', conf => '', err => 'enter a password', },
+ { new => 'secret', conf => '', err => 'do not match', },
+ { new => '', conf => 'secret', err => 'do not match', },
+ { new => 'secret', conf => 'not_secret', err => 'do not match', },
+ )
+ {
+ $mech->get_ok('/auth/change_password');
+ $mech->content_lacks( $test->{err}, "did not find expected error" );
+ $mech->submit_form_ok(
+ {
+ form_name => 'change_password',
+ fields =>
+ { new_password => $test->{new}, confirm => $test->{conf}, },
+ },
+ "change_password with '$test->{new}' and '$test->{conf}'"
+ );
+ $mech->content_contains( $test->{err}, "found expected error" );
+ }
+
+ my $user =
+ FixMyStreet::App->model('DB::User')->find( { email => $test_email } );
+ ok $user, "got a user";
+ ok !$user->password, "user has no password";
+
+ $mech->get_ok('/auth/change_password');
+ $mech->submit_form_ok(
+ {
+ form_name => 'change_password',
+ fields =>
+ { new_password => $test_password, confirm => $test_password, },
+ },
+ "change_password with '$test_password' and '$test_password'"
+ );
+ is $mech->uri->path, '/auth/change_password',
+ "still on change password page";
+ $mech->content_contains( 'password has been changed',
+ "found password changed" );
+
+ $user->discard_changes();
+ ok $user->password, "user now has a password";
+}
+
+foreach my $remember_me ( '1', '0' ) {
+ subtest "login using valid details (remember_me => '$remember_me')" => sub {
+ $mech->get_ok('/auth');
+ $mech->submit_form_ok(
+ {
+ form_name => 'general_auth',
+ fields => {
+ email => $test_email,
+ password => $test_password,
+ remember_me => ( $remember_me ? 1 : undef ),
+ },
+ button => 'login',
+ },
+ "login with '$test_email' & '$test_password"
+ );
+ is $mech->uri->path, '/my', "redirected to correct page";
+
+ # check that the cookie has no expiry set
+ my $expiry = $mech->session_cookie_expiry;
+ is( $expiry, 0, "no expiry time" );
+ #$remember_me
+ # ? cmp_ok( $expiry, '>', 86400, "long expiry time" )
+ # : is( $expiry, 0, "no expiry time" );
+
+ # logout
+ $mech->log_out_ok;
+ };
+}
+
+# try to login with bad details
+$mech->get_ok('/auth');
+$mech->submit_form_ok(
+ {
+ form_name => 'general_auth',
+ fields => {
+ email => $test_email,
+ password => 'not the password',
+ },
+ button => 'login',
+ },
+ "login with '$test_email' & '$test_password"
+);
+is $mech->uri->path, '/auth', "redirected to correct page";
+$mech->content_contains( 'Email or password wrong', 'found error message' );
+
+# more test:
+# TODO: test that email are always lowercased
diff --git a/t/app/controller/contact.t b/t/app/controller/contact.t
new file mode 100644
index 000000000..bbb3a0f83
--- /dev/null
+++ b/t/app/controller/contact.t
@@ -0,0 +1,275 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+
+my $mech = FixMyStreet::TestMech->new;
+
+$mech->get_ok('/contact');
+$mech->title_like(qr/Contact Us/);
+$mech->content_contains("We'd love to hear what you think about this site");
+
+my $problem_main;
+
+for my $test (
+ {
+ name => 'A User',
+ email => 'problem_report_test@example.com',
+ title => 'Some problem or other',
+ detail => 'More detail on the problem',
+ postcode => 'EH99 1SP',
+ confirmed => '2011-05-04 10:44:28.145168',
+ anonymous => 0,
+ meta => 'Reported by A User at 10:44, Wednesday 4 May 2011',
+ main => 1,
+ },
+ {
+ name => 'A User',
+ email => 'problem_report_test@example.com',
+ title => 'A different problem',
+ detail => 'More detail on the different problem',
+ postcode => 'EH99 1SP',
+ confirmed => '2011-05-03 13:24:28.145168',
+ anonymous => 1,
+ meta => 'Reported anonymously at 13:24, Tuesday 3 May 2011',
+ },
+ {
+ name => 'A User',
+ email => 'problem_report_test@example.com',
+ title => 'A different problem',
+ detail => 'More detail on the different problem',
+ postcode => 'EH99 1SP',
+ confirmed => '2011-05-03 13:24:28.145168',
+ anonymous => 1,
+ meta => 'Reported anonymously at 13:24, Tuesday 3 May 2011',
+ update => {
+ name => 'Different User',
+ email => 'commenter@example.com',
+ text => 'This is an update',
+ },
+ },
+ )
+{
+ subtest 'check reporting a problem displays correctly' => sub {
+ my $user = FixMyStreet::App->model('DB::User')->find_or_create(
+ {
+ name => $test->{name},
+ email => $test->{email}
+ }
+ );
+
+ my $problem = FixMyStreet::App->model('DB::Problem')->create(
+ {
+ title => $test->{title},
+ detail => $test->{detail},
+ postcode => $test->{postcode},
+ confirmed => $test->{confirmed},
+ name => $test->{name},
+ anonymous => $test->{anonymous},
+ state => 'confirmed',
+ user => $user,
+ latitude => 0,
+ longitude => 0,
+ areas => 0,
+ used_map => 0,
+ }
+ );
+
+ my $update;
+
+ if ( $test->{update} ) {
+ my $update_info = $test->{update};
+ my $update_user = FixMyStreet::App->model('DB::User')->find_or_create(
+ {
+ name => $update_info->{name},
+ email => $update_info->{email}
+ }
+ );
+
+ $update = FixMyStreet::App->model('DB::Comment')->create(
+ {
+ problem_id => $problem->id,
+ user => $update_user,
+ state => 'confirmed',
+ text => $update_info->{text},
+ confirmed => \'ms_current_timestamp()',
+ mark_fixed => 'f',
+ anonymous => 'f',
+ }
+ );
+ }
+
+ ok $problem, 'succesfully create a problem';
+
+ if ( $update ) {
+ $mech->get_ok( '/contact?id=' . $problem->id . '&update_id=' . $update->id );
+ $mech->content_contains('reporting the following update');
+ $mech->content_contains( $test->{update}->{text} );
+ } else {
+ $mech->get_ok( '/contact?id=' . $problem->id );
+ $mech->content_contains('reporting the following problem');
+ $mech->content_contains( $test->{title} );
+ $mech->content_contains( $test->{meta} );
+ }
+
+ $update->delete if $update;
+ if ($test->{main}) {
+ $problem_main = $problem;
+ } else {
+ $problem->delete;
+ }
+ };
+}
+
+for my $test (
+ {
+ fields => {
+ em => ' ',
+ name => '',
+ subject => '',
+ message => '',
+ },
+ page_errors =>
+ [ 'There were problems with your report. Please see below.', ],
+ field_errors => [
+ 'Please enter your name',
+ 'Please enter your email',
+ 'Please enter a subject',
+ 'Please write a message',
+ ]
+ },
+ {
+ fields => {
+ em => 'invalidemail',
+ name => '',
+ subject => '',
+ message => '',
+ },
+ page_errors =>
+ [ 'There were problems with your report. Please see below.', ],
+ field_errors => [
+ 'Please enter your name',
+ 'Please enter a valid email address',
+ 'Please enter a subject',
+ 'Please write a message',
+ ]
+ },
+ {
+ fields => {
+ em => 'test@example.com',
+ name => 'A name',
+ subject => '',
+ message => '',
+ },
+ page_errors =>
+ [ 'There were problems with your report. Please see below.', ],
+ field_errors => [ 'Please enter a subject', 'Please write a message', ]
+ },
+ {
+ fields => {
+ em => 'test@example.com',
+ name => 'A name',
+ subject => 'A subject',
+ message => '',
+ },
+ page_errors =>
+ [ 'There were problems with your report. Please see below.', ],
+ field_errors => [ 'Please write a message', ]
+ },
+ {
+ fields => {
+ em => 'test@example.com',
+ name => 'A name',
+ subject => ' ',
+ message => '',
+ },
+ page_errors =>
+ [ 'There were problems with your report. Please see below.', ],
+ field_errors => [ 'Please enter a subject', 'Please write a message', ]
+ },
+ {
+ fields => {
+ em => 'test@example.com',
+ name => 'A name',
+ subject => 'A subject',
+ message => ' ',
+ },
+ page_errors =>
+ [ 'There were problems with your report. Please see below.', ],
+ field_errors => [ 'Please write a message', ]
+ },
+ {
+ url => '/contact?id=' . $problem_main->id,
+ fields => {
+ em => 'test@example.com',
+ name => 'A name',
+ subject => 'A subject',
+ message => 'A message',
+ id => 'invalid',
+ },
+ page_errors => [ 'Illegal ID' ],
+ field_errors => []
+ },
+ )
+{
+ subtest 'check submit page error handling' => sub {
+ $mech->get_ok( $test->{url} ? $test->{url} : '/contact' );
+ $mech->submit_form_ok( { with_fields => $test->{fields} } );
+ is_deeply $mech->page_errors, $test->{page_errors}, 'page errors';
+ is_deeply $mech->form_errors, $test->{field_errors}, 'field_errors';
+
+ # we santise this when we submit so need to remove it
+ delete $test->{fields}->{id}
+ if $test->{fields}->{id} and $test->{fields}->{id} eq 'invalid';
+ is_deeply $mech->visible_form_values, $test->{fields}, 'form values';
+ };
+}
+
+for my $test (
+ {
+ fields => {
+ em => 'test@example.com',
+ name => 'A name',
+ subject => 'A subject',
+ message => 'A message',
+ },
+ },
+ {
+ fields => {
+ em => 'test@example.com',
+ name => 'A name',
+ subject => 'A subject',
+ message => 'A message',
+ id => $problem_main->id,
+ },
+ },
+
+ )
+{
+ subtest 'check email sent correctly' => sub {
+ $mech->clear_emails_ok;
+ if ($test->{fields}{id}) {
+ $mech->get_ok('/contact?id=' . $test->{fields}{id});
+ } else {
+ $mech->get_ok('/contact');
+ }
+ $mech->submit_form_ok( { with_fields => $test->{fields} } );
+ $mech->content_contains('Thanks for your feedback');
+ $mech->email_count_is(1);
+
+ my $email = $mech->get_email;
+
+ is $email->header('Subject'), 'FMS message: ' . $test->{fields}->{subject}, 'subject';
+ is $email->header('From'), "\"$test->{fields}->{name}\" <$test->{fields}->{em}>", 'from';
+ like $email->body, qr/$test->{fields}->{message}/, 'body';
+ like $email->body, qr/Sent by contact.cgi on \S+. IP address (?:\d{1,3}\.){3,}\d{1,3}/, 'body footer';
+ my $problem_id = $test->{fields}{id};
+ like $email->body, qr/Complaint about report $problem_id/, 'reporting a report'
+ if $test->{fields}{id};
+ };
+}
+
+$problem_main->delete;
+
+done_testing();
diff --git a/t/app/controller/council.t b/t/app/controller/council.t
new file mode 100644
index 000000000..11898995a
--- /dev/null
+++ b/t/app/controller/council.t
@@ -0,0 +1,14 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'FixMyStreet::App';
+use_ok( 'FixMyStreet::App::Controller::Council' );
+
+TODO: {
+ local $TODO = 'need to write some tests for this';
+
+}
+
+done_testing();
diff --git a/t/app/controller/index.t b/t/app/controller/index.t
new file mode 100644
index 000000000..cebeaf676
--- /dev/null
+++ b/t/app/controller/index.t
@@ -0,0 +1,58 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+my $mech = FixMyStreet::TestMech->new;
+
+# check that the homepage loads
+$mech->get_ok('/');
+
+subtest "check that the form goes to /around" => sub {
+ $mech->get_ok('/');
+ is $mech->uri->path, '/', "still on '/'";
+
+ # submit form
+ $mech->submit_form_ok( { with_fields => { pc => 'SW1A 1AA', } } );
+
+ # check that we are at /around
+ is $mech->uri->path, '/around', "Got to /around";
+ is_deeply { $mech->uri->query_form }, { pc => 'SW1A 1AA' },
+ "query passed along";
+};
+
+subtest "does pc, (x,y), (e,n) or (lat,lon) go to /around" => sub {
+
+ foreach my $test ( #
+ {
+ in => { pc => 'SW1A 1AA' },
+ out => { pc => 'SW1A 1AA' },
+ },
+ {
+ in => { lat => 51.50100, lon => -0.14158 },
+ out => { lat => 51.50100, lon => -0.14158 },
+ },
+ {
+ in => { x => 3281, y => 1113, },
+ out => { lat => 51.4998246332569, lon => -0.140137309739907 },
+ },
+ {
+ in => { e => 1234, n => 4567 },
+ out => { lat => 49.808509, lon => -7.544784 },
+ },
+ )
+ {
+
+ my $uri = URI->new('http://localhost/');
+ $uri->query_form( $test->{in} );
+
+ # get the uri and check for 302
+ $mech->get_ok($uri);
+
+ # check that we are at /around
+ is $mech->uri->path, '/around', "Got to /around";
+ is_deeply { $mech->uri->query_form }, $test->{out}, "query correct";
+ }
+};
+
+done_testing();
diff --git a/t/app/controller/json.t b/t/app/controller/json.t
new file mode 100644
index 000000000..d6820e1be
--- /dev/null
+++ b/t/app/controller/json.t
@@ -0,0 +1,109 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+use FixMyStreet::TestMech;
+my $mech = FixMyStreet::TestMech->new;
+
+subtest "check that a bad request produces the appropriate response" => sub {
+
+ my $bad_date = "Invalid dates supplied";
+ my $mad_date = "Start date after end date";
+ my $bad_type = "Invalid type supplied";
+
+ my %tests = (
+ '?' => $bad_date,
+ '?foo=bar' => $bad_date,
+ '?start_date=&end_date=' => $bad_date,
+ '?start_date=bad&end_date=2000-02-01' => $bad_date,
+ '?start_date=2000-01-01&end_date=bad' => $bad_date,
+ '?start_date=2000-02-31&end_date=2000-02-01' => $bad_date,
+ '?start_date=2000-01-01&end_date=2000-02-31' => $bad_date,
+
+ '?start_date=2000-02-01&end_date=2000-01-01' => $mad_date,
+
+ '?start_date=2000-01-01&end_date=2000-02-01' => $bad_type,
+ '/foo?type=foo&start_date=2000-01-01&end_date=2000-02-01' => $bad_type,
+ );
+
+ foreach my $q ( sort keys %tests ) {
+ is_deeply #
+ $mech->get_ok_json("/json/problems$q"), #
+ { error => $tests{$q} }, #
+ "correct error for query '$q'";
+ }
+
+};
+
+is_deeply #
+ $mech->get_ok_json(
+ "/json/problems/new?start_date=2000-01-01&end_date=2000-02-01"), #
+ [], #
+ "correct response";
+
+# put an entry in the database for this test
+my $user = $mech->create_user_ok('test@example.com');
+
+my $problem_args = {
+ postcode => 'sw1a 1aa',
+ council => '2501',
+ areas => ',105164,11806,11827,2247,2501,34817,42011,66045,70786,8519,',
+ category => 'test category',
+ title => 'Test title',
+ detail => 'Test detail',
+ used_map => 't',
+ name => 'Test Name',
+ created => '2000-01-01 12:00:00',
+ confirmed => '2000-01-01 12:01:00',
+ state => 'confirmed',
+ lang => 'en-gb',
+ service => '',
+ cobrand => '',
+ cobrand_data => '',
+ lastupdate => '2000-01-01 12:00:00',
+ whensent => undef,
+ send_questionnaire => 't',
+ latitude => '51.4531988729771',
+ longitude => '-0.23021896608596',
+};
+my $problem = $user->add_to_problems( { %$problem_args, anonymous => 0 } );
+my $anon_problem = $user->add_to_problems( { %$problem_args, anonymous => 1, confirmed => '2000-01-01 12:02:00' } );
+
+ok $problem, "created normal test problem";
+ok $anon_problem, "created anon test problem";
+
+is_deeply #
+ $mech->get_ok_json(
+ "/json/problems/new?start_date=2000-01-01&end_date=2000-02-01"), #
+ [
+ {
+ 'anonymous' => 0,
+ 'category' => 'test category',
+ 'confirmed' => '2000-01-01 12:01:00',
+ 'council' => 'Wandsworth Borough Council',
+ 'detail' => 'Test detail',
+ 'id' => $problem->id,
+ 'name' => 'Test Name',
+ 'service' => 'Web interface',
+ 'title' => 'Test title',
+ 'whensent' => undef
+ },
+ {
+ 'anonymous' => 1,
+ 'category' => 'test category',
+ 'confirmed' => '2000-01-01 12:02:00',
+ 'council' => 'Wandsworth Borough Council',
+ 'detail' => 'Test detail',
+ 'id' => $anon_problem->id,
+ 'name' => '',
+ 'service' => 'Web interface',
+ 'title' => 'Test title',
+ 'whensent' => undef
+ }
+ ],
+ "correct response";
+
+$mech->delete_user($user);
+
+done_testing();
diff --git a/t/app/controller/my.t b/t/app/controller/my.t
new file mode 100644
index 000000000..1ed6806a4
--- /dev/null
+++ b/t/app/controller/my.t
@@ -0,0 +1,19 @@
+use strict;
+use warnings;
+
+use Test::More tests => 11;
+
+use FixMyStreet::TestMech;
+my $mech = FixMyStreet::TestMech->new;
+
+$mech->get_ok('/my');
+is $mech->uri->path, '/auth', "got sent to the login page";
+
+# login
+my $user = $mech->log_in_ok( 'test@example.com' );
+$mech->get_ok('/my');
+is $mech->uri->path, '/my', "stayed on '/my/' page";
+
+# cleanup
+$mech->delete_user( $user );
+
diff --git a/t/app/controller/page_not_found.t b/t/app/controller/page_not_found.t
new file mode 100644
index 000000000..05e983109
--- /dev/null
+++ b/t/app/controller/page_not_found.t
@@ -0,0 +1,20 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 4;
+
+use Test::WWW::Mechanize::Catalyst 'FixMyStreet::App';
+
+my $mech = Test::WWW::Mechanize::Catalyst->new;
+
+# homepage ok
+$mech->get_ok('/');
+
+# get 404 page
+my $path_to_404 = '/bad/path/page_error_404_not_found';
+my $res = $mech->get($path_to_404);
+ok !$res->is_success(), "want a bad response";
+is $res->code, 404, "got 404";
+$mech->content_contains($path_to_404);
diff --git a/t/app/controller/questionnaire.t b/t/app/controller/questionnaire.t
new file mode 100644
index 000000000..a245be343
--- /dev/null
+++ b/t/app/controller/questionnaire.t
@@ -0,0 +1,292 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+use FixMyStreet::App::Controller::Questionnaire;
+
+ok( my $mech = FixMyStreet::TestMech->new, 'Created mech object' );
+
+# create a test user and report
+$mech->delete_user('test@example.com');
+
+my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'test@example.com', name => 'Test User' } );
+ok $user, "created test user";
+
+my $report_time = '2011-03-01 12:00:00';
+
+my $report = FixMyStreet::App->model('DB::Problem')->find_or_create(
+ {
+ postcode => 'EH1 1BB',
+ council => '2651',
+ areas => ',11808,135007,14419,134935,2651,20728,',
+ category => 'Street lighting',
+ title => 'Testing',
+ detail => 'Testing Detail',
+ used_map => 1,
+ name => $user->name,
+ anonymous => 0,
+ state => 'confirmed',
+ confirmed => $report_time,
+ lastupdate => $report_time,
+ whensent => '2011-03-01 12:05:00',
+ lang => 'en-gb',
+ service => '',
+ cobrand => 'default',
+ cobrand_data => '',
+ send_questionnaire => 1,
+ latitude => '55.951963',
+ longitude => '-3.189944',
+ user_id => $user->id,
+ }
+);
+my $report_id = $report->id;
+ok $report, "created test report - $report_id";
+
+# Call the questionaire sending function...
+FixMyStreet::App->model('DB::Questionnaire')->send_questionnaires( {
+ site => 'fixmystreet'
+} );
+my $email = $mech->get_email;
+ok $email, "got an email";
+like $email->body, qr/fill in our short questionnaire/i, "got questionnaire email";
+my ($token) = $email->body =~ m{http://.*?/Q/(\S+)};
+ok $token, "extracted questionnaire token '$token'";
+$mech->clear_emails_ok;
+
+$report->discard_changes;
+is $report->send_questionnaire, 0;
+
+$token = FixMyStreet::App->model("DB::Token")->find( {
+ scope => 'questionnaire', token => $token
+} );
+ok $token, 'found token for questionnaire';
+
+my $questionnaire = FixMyStreet::App->model('DB::Questionnaire')->find( {
+ id => $token->data
+} );
+ok $questionnaire, 'found questionnaire';
+
+foreach my $test (
+ {
+ desc => 'User goes to questionnaire URL with a bad token',
+ token_extra => 'BAD',
+ content => "we couldn't validate that token",
+ },
+ {
+ desc => 'User goes to questionnaire URL for a now-hidden problem',
+ state => 'hidden',
+ content => "we couldn't locate your problem",
+ },
+ {
+ desc => 'User goes to questionnaire URL for an already answered questionnaire',
+ answered => \'ms_current_timestamp()',
+ content => 'already answered this questionnaire',
+ },
+) {
+ subtest $test->{desc} => sub {
+ $report->state( $test->{state} || 'confirmed' );
+ $report->update;
+ $questionnaire->whenanswered( $test->{answered} );
+ $questionnaire->update;
+ (my $token = $token->token);
+ $token .= $test->{token_extra} if $test->{token_extra};
+ $mech->get_ok("/Q/$token");
+ $mech->content_contains( $test->{content} );
+ # Reset, no matter what test did
+ $report->state( 'confirmed' );
+ $report->update;
+ $questionnaire->whenanswered( undef );
+ $questionnaire->update;
+ };
+}
+
+$mech->get_ok("/Q/" . $token->token);
+$mech->title_like( qr/Questionnaire/ );
+$mech->submit_form_ok( );
+my @errors = @{ $mech->page_errors };
+ok scalar @errors, 'displayed error messages';
+is $errors[0], "Please state whether or not the problem has been fixed", 'error message';
+
+foreach my $test (
+ {
+ desc => 'Open report, has been fixed, first time reporter, no update left',
+ problem_state => 'confirmed',
+ fields => {
+ been_fixed => 'Yes',
+ reported => 'No',
+ },
+ comment => 'Questionnaire filled in by problem reporter',
+ },
+ {
+ desc => 'Open report, has been fixed, reported before, leaves an update',
+ problem_state => 'confirmed',
+ fields => {
+ been_fixed => 'Yes',
+ reported => 'Yes',
+ update => 'The council fixed this really quickly, thanks!',
+ },
+ },
+ {
+ desc => 'Open report, has not been fixed, not reported before, no update, asks for another questionnaire',
+ problem_state => 'confirmed',
+ fields => {
+ been_fixed => 'No',
+ reported => 'No',
+ another => 'Yes',
+ },
+ },
+ {
+ desc => 'Open report, unknown fixed, reported before, update, no further questionnaire',
+ problem_state => 'confirmed',
+ fields => {
+ been_fixed => 'Unknown',
+ reported => 'Yes',
+ update => 'This is still going on.',
+ # another => 'No', Error for not setting this tested below
+ },
+ },
+ {
+ desc => 'Fixed report, confirmed fixed, not reported before, no update',
+ problem_state => 'fixed',
+ fields => {
+ been_fixed => 'Yes',
+ reported => 'No',
+ },
+ lastupdate_static => 1,
+ },
+ {
+ desc => 'Fixed report, unknown fixed, not reported before, no update, asks for another',
+ problem_state => 'fixed',
+ fields => {
+ been_fixed => 'Unknown',
+ reported => 'No',
+ another => 'Yes',
+ },
+ },
+ {
+ desc => 'Fixed report, reopened, reported before, no update, no further questionnaire',
+ problem_state => 'fixed',
+ fields => {
+ been_fixed => 'No',
+ reported => 'Yes',
+ another => 'No',
+ # update => 'Dummy', Error for not setting this tested below
+ },
+ },
+) {
+ subtest $test->{desc} => sub {
+ $report->state ( $test->{problem_state} );
+ $report->update;
+
+ $mech->get_ok("/Q/" . $token->token);
+ $mech->title_like( qr/Questionnaire/ );
+ $mech->submit_form_ok( { with_fields => $test->{fields} } );
+
+ # If reopening, we've just submitted without an update. Should cause an error.
+ if ($test->{problem_state} eq 'fixed' && $test->{fields}{been_fixed} eq 'No') {
+ my @errors = @{ $mech->page_errors };
+ ok scalar @errors, 'displayed error messages';
+ is $errors[0], "Please provide some explanation as to why you're reopening this report", 'error message';
+ $test->{fields}{update} = 'This has not been fixed.';
+ $mech->submit_form_ok( { with_fields => $test->{fields} } );
+ }
+
+ # We forgot to say we wanted another questionnaire or not with this test
+ if ($test->{problem_state} eq 'confirmed' && $test->{fields}{been_fixed} eq 'Unknown') {
+ my @errors = @{ $mech->page_errors };
+ ok scalar @errors, 'displayed error messages';
+ is $errors[0], "Please indicate whether you'd like to receive another questionnaire", 'error message';
+ $test->{fields}{another} = 'No';
+ $mech->submit_form_ok( { with_fields => $test->{fields} } );
+ }
+
+ my $result;
+ $result = 'fixed' if $test->{fields}{been_fixed} eq 'Yes';
+ $result = 'confirmed' if $test->{fields}{been_fixed} eq 'No';
+ $result = 'unknown' if $test->{fields}{been_fixed} eq 'Unknown';
+
+ my $another = 0;
+ $another = 1 if $test->{fields}{another} && $test->{fields}{another} eq 'Yes';
+
+ # Check the right HTML page has been returned
+ $mech->content_contains( 'glad to hear it&rsquo;s been fixed' )
+ if $result eq 'fixed';
+ $mech->content_contains( 'get some more information about the status of your problem' )
+ if $result eq 'unknown';
+ $mech->content_contains( "sorry to hear that" )
+ if $result eq 'confirmed';
+
+ # Check the database has the right information
+ $report->discard_changes;
+ $questionnaire->discard_changes;
+ is $report->state, $result eq 'unknown' ? $test->{problem_state} : $result;
+ is $report->send_questionnaire, $another;
+ ok DateTime::Format::Pg->format_datetime( $report->lastupdate) gt $report_time, 'lastupdate changed'
+ unless $test->{fields}{been_fixed} eq 'Unknown' || $test->{lastupdate_static};
+ is $questionnaire->old_state, $test->{problem_state};
+ is $questionnaire->new_state, $result;
+ is $questionnaire->ever_reported, $test->{fields}{reported} eq 'Yes' ? 1 : 0;
+ if ($test->{fields}{update} || $test->{comment}) {
+ my $c = FixMyStreet::App->model("DB::Comment")->find(
+ { problem_id => $report->id }
+ );
+ is $c->text, $test->{fields}{update} || $test->{comment};
+ }
+
+ # Reset questionnaire for next test
+ $questionnaire->old_state( undef );
+ $questionnaire->new_state( undef );
+ $questionnaire->ever_reported( undef );
+ $questionnaire->whenanswered( undef );
+ $questionnaire->update;
+ $report->send_questionnaire( 0 );
+ $report->lastupdate( $report_time );
+ $report->comments->delete;
+ $report->update;
+ };
+}
+
+# EHA extra checking
+ok $mech->host("reportemptyhomes.com"), 'change host to reportemptyhomes';
+
+# Reset, and all the questionaire sending function - FIXME should it detect site itself somehow?
+$report->send_questionnaire( 1 );
+$report->update;
+$questionnaire->delete;
+FixMyStreet::App->model('DB::Questionnaire')->send_questionnaires( {
+ site => 'emptyhomes'
+} );
+$email = $mech->get_email;
+ok $email, "got an email";
+like $email->body, qr/fill in this short questionnaire/i, "got questionnaire email";
+($token) = $email->body =~ m{http://.*?/Q/(\S+)};
+ok $token, "extracted questionnaire token '$token'";
+
+$mech->get_ok("/Q/" . $token);
+$mech->content_contains( 'should have reported what they have done' );
+
+# Test already answered the ever reported question, so not shown again
+my $questionnaire2 = FixMyStreet::App->model('DB::Questionnaire')->find_or_create(
+ {
+ problem_id => $report->id,
+ whensent => '2011-03-28 12:00:00',
+ ever_reported => 1,
+ }
+);
+ok $questionnaire, 'added another questionnaire';
+ok $mech->host("fixmystreet.com"), 'change host to fixmystreet';
+$mech->get_ok("/Q/" . $token);
+$mech->title_like( qr/Questionnaire/ );
+$mech->content_contains( 'Has this problem been fixed?' );
+$mech->content_lacks( 'ever reported' );
+
+# EHA extra checking
+ok $mech->host("reportemptyhomes.com"), 'change host to reportemptyhomes';
+$mech->get_ok("/Q/" . $token);
+$mech->content_contains( 'made a lot of progress' );
+
+$mech->delete_user('test@example.com');
+done_testing();
diff --git a/t/app/controller/report_display.t b/t/app/controller/report_display.t
new file mode 100644
index 000000000..1f857a387
--- /dev/null
+++ b/t/app/controller/report_display.t
@@ -0,0 +1,265 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+use Web::Scraper;
+use Path::Class;
+use DateTime;
+
+my $mech = FixMyStreet::TestMech->new;
+
+# create a test user and report
+$mech->delete_user('test@example.com');
+my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'test@example.com', name => 'Test User' } );
+ok $user, "created test user";
+
+my $dt = DateTime->new(
+ year => 2011,
+ month => 04,
+ day => 16,
+ hour => 15,
+ minute => 47,
+ second => 23
+);
+
+my $report = FixMyStreet::App->model('DB::Problem')->find_or_create(
+ {
+ postcode => 'SW1A 1AA',
+ council => '2504',
+ areas => ',105255,11806,11828,2247,2504,',
+ category => 'Other',
+ title => 'Test 2',
+ detail => 'Test 2 Detail',
+ used_map => 't',
+ name => 'Test User',
+ anonymous => 'f',
+ state => 'confirmed',
+ confirmed => $dt->ymd . ' ' . $dt->hms,
+ lang => 'en-gb',
+ service => '',
+ cobrand => 'default',
+ cobrand_data => '',
+ send_questionnaire => 't',
+ latitude => '51.5016605453401',
+ longitude => '-0.142497580865087',
+ user_id => $user->id,
+ }
+);
+my $report_id = $report->id;
+ok $report, "created test report - $report_id";
+
+subtest "check that no id redirects to homepage" => sub {
+ $mech->get_ok('/report');
+ is $mech->uri->path, '/', "at home page";
+};
+
+subtest "test id=NNN redirects to /NNN" => sub {
+ $mech->get_ok("/report?id=$report_id");
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+};
+
+subtest "test bad council email clients web links" => sub {
+ $mech->get_ok("/report/3D$report_id");
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+};
+
+subtest "test tailing non-ints get stripped" => sub {
+ $mech->get_ok("/report/${report_id}xx ");
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+};
+
+subtest "test bad ids get dealt with (404)" => sub {
+ foreach my $id ( 'XXX', 99999999 ) {
+ ok $mech->get("/report/$id"), "get '/report/$id'";
+ is $mech->res->code, 404, "page not found";
+ is $mech->uri->path, "/report/$id", "at /report/$id";
+ $mech->content_contains('Unknown problem ID');
+ }
+};
+
+subtest "change report to unconfirmed and check for 404 status" => sub {
+ ok $report->update( { state => 'unconfirmed' } ), 'unconfirm report';
+ ok $mech->get("/report/$report_id"), "get '/report/$report_id'";
+ is $mech->res->code, 404, "page not found";
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+ $mech->content_contains('Unknown problem ID');
+ ok $report->update( { state => 'confirmed' } ), 'confirm report again';
+};
+
+subtest "change report to hidden and check for 410 status" => sub {
+ ok $report->update( { state => 'hidden' } ), 'hide report';
+ ok $mech->get("/report/$report_id"), "get '/report/$report_id'";
+ is $mech->res->code, 410, "page gone";
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+ $mech->content_contains('That report has been removed from FixMyStreet.');
+ ok $report->update( { state => 'confirmed' } ), 'confirm report again';
+};
+
+subtest "test a good report" => sub {
+ $mech->get_ok("/report/$report_id");
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+ is $mech->extract_problem_title, 'Test 2', 'problem title';
+ is $mech->extract_problem_meta,
+ 'Reported by Test User at 15:47, Saturday 16 April 2011',
+ 'correct problem meta information';
+ $mech->content_contains('Test 2 Detail');
+
+ my $update_form = $mech->form_name('updateForm');
+
+ my %fields = (
+ name => '',
+ rznvy => '',
+ update => '',
+ add_alert => 1, # defaults to true
+ fixed => undef
+ );
+ is $update_form->value($_), $fields{$_}, "$_ value" for keys %fields;
+};
+
+foreach my $meta (
+ {
+ anonymous => 'f',
+ category => 'Other',
+ service => '',
+ meta => 'Reported by Test User at 15:47, Saturday 16 April 2011'
+ },
+ {
+ anonymous => 'f',
+ category => 'Roads',
+ service => '',
+ meta =>
+'Reported in the Roads category by Test User at 15:47, Saturday 16 April 2011'
+ },
+ {
+ anonymous => 'f',
+ category => '',
+ service => 'Transport service',
+ meta =>
+'Reported by Transport service by Test User at 15:47, Saturday 16 April 2011'
+ },
+ {
+ anonymous => 'f',
+ category => 'Roads',
+ service => 'Transport service',
+ meta =>
+'Reported by Transport service in the Roads category by Test User at 15:47, Saturday 16 April 2011'
+ },
+ {
+ anonymous => 't',
+ category => 'Other',
+ service => '',
+ meta => 'Reported anonymously at 15:47, Saturday 16 April 2011'
+ },
+ {
+ anonymous => 't',
+ category => 'Roads',
+ service => '',
+ meta =>
+'Reported in the Roads category anonymously at 15:47, Saturday 16 April 2011'
+ },
+ {
+ anonymous => 't',
+ category => '',
+ service => 'Transport service',
+ meta =>
+'Reported by Transport service anonymously at 15:47, Saturday 16 April 2011'
+ },
+ {
+ anonymous => 't',
+ category => 'Roads',
+ service => 'Transport service',
+ meta =>
+'Reported by Transport service in the Roads category anonymously at 15:47, Saturday 16 April 2011'
+ },
+ )
+{
+ $report->service( $meta->{service} );
+ $report->category( $meta->{category} );
+ $report->anonymous( $meta->{anonymous} );
+ $report->update;
+ subtest "test correct problem meta information" => sub {
+ $mech->get_ok("/report/$report_id");
+
+ is $mech->extract_problem_meta, $meta->{meta};
+
+ };
+}
+
+for my $test (
+ {
+ description => 'new report',
+ date => DateTime->now,
+ state => 'confirmed',
+ banner_id => '',
+ banner_text => '',
+ fixed => 0
+ },
+ {
+ description => 'old report',
+ date => DateTime->new(
+ year => 2009,
+ month => 6,
+ day => 12,
+ hour => 9,
+ minute => 43,
+ second => 12
+ ),
+ state => 'confirmed',
+ banner_id => 'unknown',
+ banner_text => 'This problem is old and of unknown status.',
+ fixed => 0
+ },
+ {
+ description => 'old fixed report',
+ date => DateTime->new(
+ year => 2009,
+ month => 6,
+ day => 12,
+ hour => 9,
+ minute => 43,
+ second => 12
+ ),
+ state => 'fixed',
+ banner_id => 'fixed',
+ banner_text => 'This problem has been fixed.',
+ fixed => 1
+ },
+ {
+ description => 'fixed report',
+ date => DateTime->now,
+ state => 'fixed',
+ banner_id => 'fixed',
+ banner_text => 'This problem has been fixed.',
+ fixed => 1
+ },
+) {
+ subtest "banner for $test->{description}" => sub {
+ $report->confirmed( $test->{date}->ymd . ' ' . $test->{date}->hms );
+ $report->lastupdate( $test->{date}->ymd . ' ' . $test->{date}->hms );
+ $report->state( $test->{state} );
+ $report->update;
+
+ $mech->get_ok("/report/$report_id");
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+ my $banner = $mech->extract_problem_banner;
+ $banner->{text} =~ s/^ //g;
+ $banner->{text} =~ s/ $//g;
+
+ is $banner->{id}, $test->{banner_id}, 'banner id';
+ is $banner->{text}, $test->{banner_text}, 'banner text';
+
+ my $update_form = $mech->form_name( 'updateForm' );
+ if ( $test->{fixed} ) {
+ is $update_form->find_input( 'fixed' ), undef, 'problem is fixed';
+ } else {
+ ok $update_form->find_input( 'fixed' ), 'problem is not fixed';
+ }
+ };
+}
+
+# tidy up
+$mech->delete_user('test@example.com');
+done_testing();
diff --git a/t/app/controller/report_import.t b/t/app/controller/report_import.t
new file mode 100644
index 000000000..154de13d8
--- /dev/null
+++ b/t/app/controller/report_import.t
@@ -0,0 +1,242 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+use Web::Scraper;
+use Path::Class;
+
+my $mech = FixMyStreet::TestMech->new;
+$mech->get_ok('/import');
+
+my $sample_file = file(__FILE__)->parent->file("sample.jpg")->stringify;
+ok -e $sample_file, "sample file $sample_file exists";
+
+# submit an empty report to import - check we get all errors
+subtest "Test creating bad partial entries" => sub {
+
+ foreach my $test (
+ {
+ fields => { email => 'bob', },
+ errors => [
+ 'You must supply a service',
+ 'Please enter a subject',
+ 'Please enter your name',
+ 'Please enter a valid email',
+ 'Either a location or a photo must be provided.',
+ ],
+ },
+ {
+ fields => { email => 'bob@example.com' },
+ errors => [
+ 'You must supply a service',
+ 'Please enter a subject',
+ 'Please enter your name',
+ 'Either a location or a photo must be provided.',
+ ],
+ },
+ {
+ fields => { lat => 1, lon => 1, },
+ errors => [
+ 'You must supply a service',
+ 'Please enter a subject',
+ 'Please enter your name',
+ 'Please enter your email',
+'We had a problem with the supplied co-ordinates - outside the UK?',
+ ],
+ },
+ {
+ fields => { photo => $sample_file, },
+ errors => [
+ 'You must supply a service',
+ 'Please enter a subject',
+ 'Please enter your name',
+ 'Please enter your email',
+ ],
+ },
+ )
+ {
+ $mech->get_ok('/import');
+
+ $mech->submit_form_ok( #
+ { with_fields => $test->{fields} },
+ "fill in form"
+ );
+
+ is_deeply( $mech->import_errors, $test->{errors}, "expected errors" );
+ }
+
+};
+
+# submit an empty report to import - check we get all errors
+subtest "Submit a correct entry" => sub {
+
+ $mech->get_ok('/import');
+
+ $mech->submit_form_ok( #
+ {
+ with_fields => {
+ service => 'test-script',
+ name => 'Test User',
+ email => 'test@example.com',
+ subject => 'Test report',
+ detail => 'This is a test report',
+ photo => $sample_file,
+ }
+ },
+ "fill in form"
+ );
+
+ is_deeply( $mech->import_errors, [], "got no errors" );
+ is $mech->content, 'SUCCESS', "Got success response";
+
+ # check that we have received the email
+ $mech->email_count_is(1);
+ my $email = $mech->get_email;
+ $mech->clear_emails_ok;
+
+ my ($token_url) = $email->body =~ m{(http://\S+)};
+ ok $token_url, "Found a token url $token_url";
+
+ # go to the token url
+ $mech->get_ok($token_url);
+
+ # check that we are on '/around'
+ is $mech->uri->path, '/around', "sent to /around";
+
+ # check that we are not shown anything as we don't have a location yet
+ is_deeply $mech->visible_form_values, { pc => '' },
+ "check only pc field is shown";
+
+ $mech->submit_form_ok( #
+ { with_fields => { pc => 'SW1A 1AA' } },
+ "fill in postcode"
+ );
+
+ is $mech->uri->path, '/report/new', "sent to report page";
+
+ # check that fields are prefilled for us
+ is_deeply $mech->visible_form_values,
+ {
+ name => 'Test User',
+ email => 'test@example.com',
+ title => 'Test report',
+ detail => 'This is a test report',
+ photo => '',
+ phone => '',
+ may_show_name => '1',
+ category => '-- Pick a category --',
+ },
+ "check imported fields are shown";
+
+ # change the details
+ $mech->submit_form_ok( #
+ {
+ with_fields => {
+ name => 'New Test User',
+ email => 'test@example.com',
+ title => 'New Test report',
+ detail => 'This is a test report',
+ phone => '01234 567 890',
+ may_show_name => '1',
+ category => 'Street lighting',
+ }
+ },
+ "Update details and save"
+ );
+
+ # check that report has been created
+ my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => 'test@example.com' } );
+ ok $user, "Found a user";
+
+ my $report = $user->problems->first;
+ is $report->state, 'confirmed', 'is confirmed';
+ is $report->title, 'New Test report', 'title is correct';
+
+ $mech->delete_user($user);
+};
+
+# submit an empty report to import - check we get all errors
+subtest "Submit a correct entry (with location)" => sub {
+
+ $mech->get_ok('/import');
+
+ $mech->submit_form_ok( #
+ {
+ with_fields => {
+ service => 'test-script',
+ lat => '51.5010096115539', # SW1A 1AA
+ lon => '-0.141587067110009',
+ name => 'Test User ll',
+ email => 'test-ll@example.com',
+ subject => 'Test report ll',
+ detail => 'This is a test report ll',
+ photo => $sample_file,
+ }
+ },
+ "fill in form"
+ );
+
+ is_deeply( $mech->import_errors, [], "got no errors" );
+ is $mech->content, 'SUCCESS', "Got success response";
+
+ # check that we have received the email
+ $mech->email_count_is(1);
+ my $email = $mech->get_email;
+ $mech->clear_emails_ok;
+
+ my ($token_url) = $email->body =~ m{(http://\S+)};
+ ok $token_url, "Found a token url $token_url";
+
+ # go to the token url
+ $mech->get_ok($token_url);
+
+ # check that we are on '/around'
+ is $mech->uri->path, '/report/new', "sent to /around";
+
+ # check that fields are prefilled for us
+ is_deeply $mech->visible_form_values,
+ {
+ name => 'Test User ll',
+ email => 'test-ll@example.com',
+ title => 'Test report ll',
+ detail => 'This is a test report ll',
+ photo => '',
+ phone => '',
+ may_show_name => '1',
+ category => '-- Pick a category --',
+ },
+ "check imported fields are shown";
+
+ # change the details
+ $mech->submit_form_ok( #
+ {
+ with_fields => {
+ name => 'New Test User ll',
+ email => 'test-ll@example.com',
+ title => 'New Test report ll',
+ detail => 'This is a test report ll',
+ phone => '01234 567 890',
+ may_show_name => '1',
+ category => 'Street lighting',
+ }
+ },
+ "Update details and save"
+ );
+
+ # check that report has been created
+ my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => 'test-ll@example.com' } );
+ ok $user, "Found a user";
+
+ my $report = $user->problems->first;
+ is $report->state, 'confirmed', 'is confirmed';
+ is $report->title, 'New Test report ll', 'title is correct';
+
+ $mech->delete_user($user);
+};
+
+done_testing();
diff --git a/t/app/controller/report_new.t b/t/app/controller/report_new.t
new file mode 100644
index 000000000..01c29ecf4
--- /dev/null
+++ b/t/app/controller/report_new.t
@@ -0,0 +1,501 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+use Web::Scraper;
+
+my $mech = FixMyStreet::TestMech->new;
+$mech->get_ok('/report/new');
+
+subtest "test that bare requests to /report/new get redirected" => sub {
+
+ $mech->get_ok('/report/new');
+ is $mech->uri->path, '/around', "went to /around";
+ is_deeply { $mech->uri->query_form }, {}, "query empty";
+
+ $mech->get_ok('/report/new?pc=SW1A%201AA');
+ is $mech->uri->path, '/around', "went to /around";
+ is_deeply { $mech->uri->query_form }, { pc => 'SW1A 1AA' },
+ "pc correctly transferred";
+};
+
+my %contact_params = (
+ confirmed => 1,
+ deleted => 0,
+ editor => 'Test',
+ whenedited => \'current_timestamp',
+ note => 'Created for test',
+);
+# Let's make some contacts to send things to!
+my $contact1 = FixMyStreet::App->model('DB::Contact')->find_or_create( {
+ %contact_params,
+ area_id => 2651, # Edinburgh
+ category => 'Street lighting',
+ email => 'highways@example.com',
+} );
+my $contact2 = FixMyStreet::App->model('DB::Contact')->find_or_create( {
+ %contact_params,
+ area_id => 2226, # Gloucestershire
+ category => 'Potholes',
+ email => 'potholes@example.com',
+} );
+my $contact3 = FixMyStreet::App->model('DB::Contact')->find_or_create( {
+ %contact_params,
+ area_id => 2326, # Cheltenham
+ category => 'Trees',
+ email => 'trees@example.com',
+} );
+ok $contact1, "created test contact 1";
+ok $contact2, "created test contact 2";
+ok $contact3, "created test contact 3";
+
+# test that the various bit of form get filled in and errors correctly
+# generated.
+foreach my $test (
+ {
+ msg => 'all fields empty',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => '',
+ detail => '',
+ photo => '',
+ name => '',
+ may_show_name => '1',
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ },
+ changes => {},
+ errors => [
+ 'Please enter a subject',
+ 'Please enter some details',
+ 'Please enter your name',
+ 'Please enter your email',
+ ],
+ },
+ {
+ msg => 'may_show_name defaults to true',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => '',
+ detail => '',
+ photo => '',
+ name => '',
+ may_show_name => undef,
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ },
+ changes => { may_show_name => '1' },
+ errors => [
+ 'Please enter a subject',
+ 'Please enter some details',
+ 'Please enter your name',
+ 'Please enter your email',
+ ],
+ },
+ {
+ msg => 'may_show_name unchanged if name is present (stays false)',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => '',
+ detail => '',
+ photo => '',
+ name => 'Bob Jones',
+ may_show_name => undef,
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ },
+ changes => {},
+ errors => [
+ 'Please enter a subject',
+ 'Please enter some details',
+ 'Please enter your email',
+ ],
+ },
+ {
+ msg => 'may_show_name unchanged if name is present (stays true)',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => '',
+ detail => '',
+ photo => '',
+ name => 'Bob Jones',
+ may_show_name => '1',
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ },
+ changes => {},
+ errors => [
+ 'Please enter a subject',
+ 'Please enter some details',
+ 'Please enter your email',
+ ],
+ },
+ {
+ msg => 'title and details tidied up',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => 'DOG SHIT ON WALLS',
+ detail => 'on this portakabin - more of a portaloo HEH!!',
+ photo => '',
+ name => 'Bob Jones',
+ may_show_name => '1',
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ },
+ changes => {
+ title => 'Dog poo on walls',
+ detail =>
+ 'On this [portable cabin] - more of a [portable loo] HEH!!',
+ },
+ errors => [ 'Please enter your email', ],
+ },
+ {
+ msg => 'name too short',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => 'Test title',
+ detail => 'Test detail',
+ photo => '',
+ name => 'DUDE',
+ may_show_name => '1',
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ },
+ changes => {},
+ errors => [
+'Please enter your full name, councils need this information - if you do not wish your name to be shown on the site, untick the box',
+ 'Please enter your email',
+ ],
+ },
+ {
+ msg => 'name is anonymous',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => 'Test title',
+ detail => 'Test detail',
+ photo => '',
+ name => 'anonymous',
+ may_show_name => '1',
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ },
+ changes => {},
+ errors => [
+'Please enter your full name, councils need this information - if you do not wish your name to be shown on the site, untick the box',
+ 'Please enter your email',
+ ],
+ },
+ {
+ msg => 'email invalid',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => 'Test title',
+ detail => 'Test detail',
+ photo => '',
+ name => 'Joe Smith',
+ may_show_name => '1',
+ email => 'not an email',
+ phone => '',
+ category => 'Street lighting',
+ },
+ changes => { email => 'notanemail', },
+ errors => [ 'Please enter a valid email', ],
+ },
+ {
+ msg => 'cleanup title and detail',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => " Test title ",
+ detail => " first line \n\n second\nline\n\n ",
+ photo => '',
+ name => '',
+ may_show_name => '1',
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ },
+ changes => {
+ title => 'Test title',
+ detail => "First line\n\nSecond line",
+ },
+ errors => [ 'Please enter your name', 'Please enter your email', ],
+ },
+ {
+ msg => 'clean up name and email',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => '',
+ detail => '',
+ photo => '',
+ name => ' Bob Jones ',
+ may_show_name => '1',
+ email => ' BOB @ExAmplE.COM ',
+ phone => '',
+ category => 'Street lighting',
+ },
+ changes => {
+ name => 'Bob Jones',
+ email => 'bob@example.com',
+ },
+ errors => [ 'Please enter a subject', 'Please enter some details', ],
+ },
+ )
+{
+ subtest "check form errors where $test->{msg}" => sub {
+ $mech->get_ok('/around');
+
+ # submit initial pc form
+ $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } },
+ "submit location" );
+ is_deeply $mech->form_errors, [], "no errors for pc '$test->{pc}'";
+
+ # click through to the report page
+ $mech->follow_link_ok( { text => 'skip this step', },
+ "follow 'skip this step' link" );
+
+ # submit the main form
+ $mech->submit_form_ok( { with_fields => $test->{fields} },
+ "submit form" );
+
+ # check that we got the errors expected
+ is_deeply $mech->form_errors, $test->{errors}, "check errors";
+
+ # check that fields have changed as expected
+ my $new_values = {
+ %{ $test->{fields} }, # values added to form
+ %{ $test->{changes} }, # changes we expect
+ };
+ is_deeply $mech->visible_form_values, $new_values,
+ "values correctly changed";
+ };
+}
+
+subtest "test report creation for a user who does not have an account" => sub {
+ $mech->log_out_ok;
+ $mech->clear_emails_ok;
+
+ # check that the user does not exist
+ my $test_email = 'test-1@example.com';
+ ok !FixMyStreet::App->model('DB::User')->find( { email => $test_email } ),
+ "test user does not exist";
+
+ # submit initial pc form
+ $mech->get_ok('/around');
+ $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } },
+ "submit location" );
+
+ # click through to the report page
+ $mech->follow_link_ok( { text => 'skip this step', },
+ "follow 'skip this step' link" );
+
+ $mech->submit_form_ok(
+ {
+ with_fields => {
+ title => 'Test Report',
+ detail => 'Test report details.',
+ photo => '',
+ name => 'Joe Bloggs',
+ may_show_name => '1',
+ email => 'test-1@example.com',
+ phone => '07903 123 456',
+ category => 'Street lighting',
+ }
+ },
+ "submit good details"
+ );
+
+ # check that we got the errors expected
+ is_deeply $mech->form_errors, [], "check there were no errors";
+
+ # check that the user has been created
+ my $user =
+ FixMyStreet::App->model('DB::User')->find( { email => $test_email } );
+ ok $user, "created new user";
+
+ # find the report
+ my $report = $user->problems->first;
+ ok $report, "Found the report";
+
+ # check that the report is not available yet.
+ is $report->state, 'unconfirmed', "report not confirmed";
+ is $mech->get( '/report/' . $report->id )->code, 404, "report not found";
+
+ # Check the report has been assigned appropriately
+ is $report->council, 2651;
+
+ # receive token
+ my $email = $mech->get_email;
+ ok $email, "got an email";
+ like $email->body, qr/confirm the problem/i, "confirm the problem";
+
+ my ($url) = $email->body =~ m{(http://\S+)};
+ ok $url, "extracted confirm url '$url'";
+
+ # confirm token
+ $mech->get_ok($url);
+ $report->discard_changes;
+ is $report->state, 'confirmed', "Report is now confirmed";
+
+ $mech->get_ok( '/report/' . $report->id );
+
+ # check that the reporter has an alert
+ my $alert = FixMyStreet::App->model('DB::Alert')->find( {
+ user => $report->user,
+ alert_type => 'new_updates',
+ parameter => $report->id,
+ } );
+ ok $alert, "created new alert";
+
+ # user is created and logged in
+ $mech->logged_in_ok;
+
+ # cleanup
+ $mech->delete_user($user);
+};
+
+#### test report creation for a user who has account but is not logged in
+# come to site
+# fill in report
+# receive token
+# confirm token
+# report is confirmed
+
+#### test report creation for user with account and logged in
+foreach my $test (
+ { category => 'Trees', council => 2326 },
+ { category => 'Potholes', council => 2226 },
+) {
+ subtest "test report creation for a user who is logged in" => sub {
+
+ # check that the user does not exist
+ my $test_email = 'test-2@example.com';
+
+ $mech->clear_emails_ok;
+ my $user = $mech->log_in_ok($test_email);
+
+ # setup the user.
+ ok $user->update(
+ {
+ name => 'Test User',
+ phone => '01234 567 890',
+ }
+ ),
+ "set users details";
+
+ # submit initial pc form
+ $mech->get_ok('/around');
+ $mech->submit_form_ok( { with_fields => { pc => 'GL50 2PR', } },
+ "submit location" );
+
+ # click through to the report page
+ $mech->follow_link_ok( { text => 'skip this step', },
+ "follow 'skip this step' link" );
+
+ # check that the fields are correctly prefilled
+ is_deeply(
+ $mech->visible_form_values,
+ {
+ title => '',
+ detail => '',
+ may_show_name => '1',
+ email => $test_email,
+ name => 'Test User',
+ phone => '01234 567 890',
+ photo => '',
+ category => '-- Pick a category --',
+ },
+ "user's details prefilled"
+ );
+
+ $mech->submit_form_ok(
+ {
+ with_fields => {
+ title => 'Test Report',
+ detail => 'Test report details.',
+ photo => '',
+ name => 'Joe Bloggs',
+ may_show_name => '1',
+ phone => '07903 123 456',
+ category => $test->{category},
+ }
+ },
+ "submit good details"
+ );
+
+ # find the report
+ my $report = $user->problems->first;
+ ok $report, "Found the report";
+
+ # Check the report has been assigned appropriately
+ is $report->council, $test->{council};
+
+ # check that we got redirected to /report/
+ is $mech->uri->path, "/report/" . $report->id, "redirected to report page";
+
+ # check that no emails have been sent
+ $mech->email_count_is(0);
+
+ # check report is confirmed and available
+ is $report->state, 'confirmed', "report is now confirmed";
+ $mech->get_ok( '/report/' . $report->id );
+
+ # check that the reporter has an alert
+ my $alert = FixMyStreet::App->model('DB::Alert')->find( {
+ user => $report->user,
+ alert_type => 'new_updates',
+ parameter => $report->id,
+ } );
+ ok $alert, "created new alert";
+
+ # user is still logged in
+ $mech->logged_in_ok;
+
+ # cleanup
+ $mech->delete_user($user);
+ };
+
+}
+
+#### test uploading an image
+
+#### test completing a partial report (eq flickr upload)
+
+#### possibly manual testing
+# create report without using map
+# create report by clicking on may with javascript off
+# create report with images off
+
+subtest "check that a lat/lon off coast leads to /around" => sub {
+ my $off_coast_latitude = 50.78301;
+ my $off_coast_longitude = -0.646929;
+
+ $mech->get_ok( #
+ "/report/new"
+ . "?latitude=$off_coast_latitude"
+ . "&longitude=$off_coast_longitude"
+ );
+
+ is $mech->uri->path, '/around', "redirected to '/around'";
+
+ is_deeply #
+ $mech->page_errors,
+ [ 'That spot does not appear to be covered by a council. If you have'
+ . ' tried to report an issue past the shoreline, for example, please'
+ . ' specify the closest point on land.' ], #
+ "Found location error";
+
+};
+
+$contact1->delete;
+$contact2->delete;
+$contact3->delete;
+
+done_testing();
diff --git a/t/app/controller/report_updates.t b/t/app/controller/report_updates.t
new file mode 100644
index 000000000..4dd1db737
--- /dev/null
+++ b/t/app/controller/report_updates.t
@@ -0,0 +1,885 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+use Web::Scraper;
+use Path::Class;
+use DateTime;
+
+my $mech = FixMyStreet::TestMech->new;
+
+# create a test user and report
+$mech->delete_user('commenter@example.com');
+$mech->delete_user('test@example.com');
+
+my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'test@example.com', name => 'Test User' } );
+ok $user, "created test user";
+
+my $user2 =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'commenter@example.com', name => 'Commenter' } );
+ok $user2, "created comment user";
+
+my $dt = DateTime->new(
+ year => 2011,
+ month => 04,
+ day => 16,
+ hour => 15,
+ minute => 47,
+ second => 23
+);
+
+my $report = FixMyStreet::App->model('DB::Problem')->find_or_create(
+ {
+ postcode => 'SW1A 1AA',
+ council => '2504',
+ areas => ',105255,11806,11828,2247,2504,',
+ category => 'Other',
+ title => 'Test 2',
+ detail => 'Test 2 Detail',
+ used_map => 't',
+ name => 'Test User',
+ anonymous => 'f',
+ state => 'confirmed',
+ confirmed => $dt->ymd . ' ' . $dt->hms,
+ lang => 'en-gb',
+ service => '',
+ cobrand => 'default',
+ cobrand_data => '',
+ send_questionnaire => 't',
+ latitude => '51.5016605453401',
+ longitude => '-0.142497580865087',
+ user_id => $user->id,
+ }
+);
+my $report_id = $report->id;
+ok $report, "created test report - $report_id";
+
+my $comment = FixMyStreet::App->model('DB::Comment')->find_or_create(
+ {
+ problem_id => $report_id,
+ user_id => $user2->id,
+ name => 'Other User',
+ mark_fixed => 'false',
+ text => 'This is some update text',
+ state => 'confirmed',
+ confirmed => $dt->ymd . ' ' . $dt->hms,
+ anonymous => 'f',
+ }
+);
+
+my $comment_id = $comment->id;
+ok $comment, "created test update - $comment_id";
+
+for my $test (
+ {
+ description => 'named user, anon is false',
+ name => 'Other User',
+ anonymous => 'f',
+ mark_fixed => 'false',
+ mark_open => 'false',
+ meta => 'Posted by Other User at 15:47, Saturday 16 April 2011',
+ },
+ {
+ description => 'blank user, anon is false',
+ name => '',
+ anonymous => 'f',
+ mark_fixed => 'false',
+ mark_open => 'false',
+ meta => 'Posted anonymously at 15:47, Saturday 16 April 2011',
+ },
+ {
+ description => 'named user, anon is true',
+ name => 'Other User',
+ anonymous => 't',
+ mark_fixed => 'false',
+ mark_open => 'false',
+ meta => 'Posted anonymously at 15:47, Saturday 16 April 2011',
+ },
+ {
+ description => 'named user, anon is true, fixed',
+ name => 'Other User',
+ anonymous => 't',
+ mark_fixed => 'true',
+ mark_open => 'false',
+ meta =>
+'Posted anonymously at 15:47, Saturday 16 April 2011, marked as fixed',
+ },
+ {
+ description => 'named user, anon is true, reopened',
+ name => 'Other User',
+ anonymous => 't',
+ mark_fixed => 'false',
+ mark_open => 'true',
+ meta => 'Posted anonymously at 15:47, Saturday 16 April 2011, reopened',
+ }
+ )
+{
+ subtest "test update displayed for $test->{description}" => sub {
+ $comment->name( $test->{name} );
+ $comment->mark_fixed( $test->{mark_fixed} );
+ $comment->mark_open( $test->{mark_open} );
+ $comment->anonymous( $test->{anonymous} );
+ $comment->update;
+
+ $mech->get_ok("/report/$report_id");
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+ $mech->content_contains('This is some update text');
+
+ my $meta = $mech->extract_update_metas;
+ is scalar @$meta, 1, 'number of updates';
+ is $meta->[0], $test->{meta};
+ };
+}
+
+subtest "unconfirmed updates not displayed" => sub {
+ $comment->state( 'unconfirmed' );
+ $comment->update;
+ $mech->get_ok("/report/$report_id");
+
+ my $meta = $mech->extract_update_metas;
+ is scalar @$meta, 0, 'update not displayed';
+};
+
+subtest "several updates shown in correct order" => sub {
+ for my $fields ( {
+ problem_id => $report_id,
+ user_id => $user2->id,
+ name => 'Other User',
+ mark_fixed => 'false',
+ text => 'First update',
+ state => 'confirmed',
+ confirmed => '2011-03-10 12:23:15',
+ anonymous => 'f',
+ },
+ {
+ problem_id => $report_id,
+ user_id => $user->id,
+ name => 'Main User',
+ mark_fixed => 'false',
+ text => 'Second update',
+ state => 'confirmed',
+ confirmed => '2011-03-10 12:23:16',
+ anonymous => 'f',
+ },
+ {
+ problem_id => $report_id,
+ user_id => $user->id,
+ name => 'Other User',
+ anonymous => 'true',
+ mark_fixed => 'true',
+ text => 'Third update',
+ state => 'confirmed',
+ confirmed => '2011-03-15 08:12:36',
+ }
+ ) {
+ my $comment = FixMyStreet::App->model('DB::Comment')->find_or_create(
+ $fields
+ );
+ }
+
+ $mech->get_ok("/report/$report_id");
+
+ my $meta = $mech->extract_update_metas;
+ is scalar @$meta, 3, 'number of updates';
+ is $meta->[0], 'Posted by Other User at 12:23, Thursday 10 March 2011', 'first update';
+ is $meta->[1], 'Posted by Main User at 12:23, Thursday 10 March 2011', 'second update';
+ is $meta->[2], 'Posted anonymously at 08:12, Tuesday 15 March 2011, marked as fixed', 'third update';
+};
+
+for my $test (
+ {
+ desc => 'No email, no message',
+ fields => {
+ rznvy => '',
+ update => '',
+ name => '',
+ photo => '',
+ fixed => undef,
+ add_alert => 1,
+ may_show_name => undef,
+ },
+ changes => {},
+ field_errors => [ 'Please enter your email', 'Please enter a message' ]
+ },
+ {
+ desc => 'Invalid email, no message',
+ fields => {
+ rznvy => 'test',
+ update => '',
+ name => '',
+ photo => '',
+ fixed => undef,
+ add_alert => 1,
+ may_show_name => undef,
+ },
+ changes => {},
+ field_errors => [ 'Please enter a valid email', 'Please enter a message' ]
+ },
+ {
+ desc => 'email with spaces, no message',
+ fields => {
+ rznvy => 'test @ example. com',
+ update => '',
+ name => '',
+ photo => '',
+ fixed => undef,
+ add_alert => 1,
+ may_show_name => undef,
+ },
+ changes => {
+ rznvy => 'test@example.com',
+ },
+ field_errors => [ 'Please enter a message' ]
+ },
+ {
+ desc => 'email with uppercase, no message',
+ fields => {
+ rznvy => 'test@EXAMPLE.COM',
+ update => '',
+ name => '',
+ photo => '',
+ fixed => undef,
+ add_alert => 1,
+ may_show_name => undef,
+ },
+ changes => {
+ rznvy => 'test@example.com',
+ },
+ field_errors => [ 'Please enter a message' ]
+ },
+ )
+{
+ subtest "submit an update - $test->{desc}" => sub {
+ $mech->get_ok("/report/$report_id");
+
+ $mech->submit_form_ok( { with_fields => $test->{fields} },
+ 'submit update' );
+
+ is_deeply $mech->form_errors, $test->{field_errors}, 'field errors';
+
+ my $values = {
+ %{ $test->{fields} },
+ %{ $test->{changes} },
+ };
+
+ is_deeply $mech->visible_form_values('updateForm'), $values, 'form changes';
+ };
+}
+
+for my $test (
+ {
+ desc => 'submit an update for a non registered user',
+ initial_values => {
+ name => '',
+ rznvy => '',
+ may_show_name => undef,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ },
+ form_values => {
+ submit_update => 1,
+ rznvy => 'unregistered@example.com',
+ update => 'Update from an unregistered user',
+ add_alert => undef,
+ },
+ changes => {},
+ },
+ {
+ desc => 'submit an update for a non registered user and sign up',
+ initial_values => {
+ name => '',
+ rznvy => '',
+ may_show_name => undef,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ },
+ form_values => {
+ submit_update => 1,
+ rznvy => 'unregistered@example.com',
+ update => 'update from an unregistered user',
+ add_alert => 1,
+ },
+ changes => {
+ update => 'Update from an unregistered user',
+ },
+ }
+) {
+ subtest $test->{desc} => sub {
+ $mech->log_out_ok();
+ $mech->clear_emails_ok();
+
+ $mech->get_ok("/report/$report_id");
+
+ my $values = $mech->visible_form_values('updateForm');
+
+ is_deeply $values, $test->{initial_values}, 'initial form values';
+
+ $mech->submit_form_ok(
+ {
+ with_fields => $test->{form_values}
+ },
+ 'submit update'
+ );
+
+ $mech->content_contains('Nearly Done! Now check your email');
+
+ my $email = $mech->get_email;
+ ok $email, "got an email";
+ like $email->body, qr/confirm the update you/i, "Correct email text";
+
+ my ( $url, $url_token ) = $email->body =~ m{(http://\S+/C/)(\S+)};
+ ok $url, "extracted confirm url '$url'";
+
+ my $token = FixMyStreet::App->model('DB::Token')->find(
+ {
+ token => $url_token,
+ scope => 'comment'
+ }
+ );
+ ok $token, 'Token found in database';
+
+ my $update_id = $token->data->{id};
+ my $add_alerts = $token->data->{add_alert};
+ my $update =
+ FixMyStreet::App->model('DB::Comment')->find( { id => $update_id } );
+
+ my $details = {
+ %{ $test->{form_values} },
+ %{ $test->{changes} }
+ };
+
+ ok $update, 'found update in database';
+ is $update->state, 'unconfirmed', 'update unconfirmed';
+ is $update->user->email, $details->{rznvy}, 'update email';
+ is $update->text, $details->{update}, 'update text';
+ is $add_alerts, $details->{add_alert} ? 1 : 0, 'do not sign up for alerts';
+
+ $mech->get_ok( $url . $url_token );
+ $mech->content_contains("/report/$report_id#update_$update_id");
+
+ my $unreg_user = FixMyStreet::App->model( 'DB::User' )->find( { email => $details->{rznvy} } );
+
+ ok $unreg_user, 'found user';
+
+ my $alert = FixMyStreet::App->model( 'DB::Alert' )->find(
+ { user => $unreg_user, alert_type => 'new_updates', confirmed => 1, }
+ );
+
+ ok $details->{add_alert} ? defined( $alert ) : !defined( $alert ), 'sign up for alerts';
+
+ $update->discard_changes;
+
+ is $update->state, 'confirmed', 'update confirmed';
+ $mech->delete_user( $unreg_user );
+ };
+}
+
+for my $test (
+ {
+ desc => 'submit update for register user',
+ initial_values => {
+ name => 'Test User',
+ rznvy => 'test@example.com',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ },
+ fields => {
+ submit_update => 1,
+ rznvy => 'test@example.com',
+ update => 'update from a registered user',
+ add_alert => undef,
+ fixed => undef,
+ },
+ changed => {
+ update => 'Update from a registered user'
+ },
+ initial_banner => '',
+ endstate_banner => '',
+ alert => 0,
+ anonymous => 0,
+ },
+ {
+ desc => 'submit update for register user anonymously by unchecking',
+ initial_values => {
+ name => 'Test User',
+ rznvy => 'test@example.com',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ },
+ fields => {
+ submit_update => 1,
+ rznvy => 'test@example.com',
+ update => 'update from a registered user',
+ may_show_name => undef,
+ add_alert => undef,
+ fixed => undef,
+ },
+ changed => {
+ update => 'Update from a registered user'
+ },
+ initial_banner => '',
+ endstate_banner => '',
+ alert => 0,
+ anonymous => 1,
+ },
+ {
+ desc => 'submit update for register user anonymously by deleting name',
+ initial_values => {
+ name => 'Test User',
+ rznvy => 'test@example.com',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ },
+ fields => {
+ submit_update => 1,
+ name => '',
+ rznvy => 'test@example.com',
+ update => 'update from a registered user',
+ may_show_name => 1,
+ add_alert => undef,
+ fixed => undef,
+ },
+ changed => {
+ update => 'Update from a registered user'
+ },
+ initial_banner => '',
+ endstate_banner => '',
+ alert => 0,
+ anonymous => 1,
+ },
+ {
+ desc => 'submit update for register user and sign up',
+ initial_values => {
+ name => 'Test User',
+ rznvy => 'test@example.com',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ },
+ fields => {
+ submit_update => 1,
+ rznvy => 'test@example.com',
+ update => 'update from a registered user',
+ add_alert => 1,
+ fixed => undef,
+ },
+ changed => {
+ update => 'Update from a registered user'
+ },
+ initial_banner => '',
+ endstate_banner => '',
+ alert => 1,
+ anonymous => 0,
+ },
+ {
+ desc => 'submit update for register user and mark fixed',
+ initial_values => {
+ name => 'Commenter',
+ rznvy => 'commenter@example.com',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ },
+ fields => {
+ submit_update => 1,
+ rznvy => 'commenter@example.com',
+ update => 'update from a registered user',
+ add_alert => undef,
+ fixed => 1,
+ },
+ changed => {
+ update => 'Update from a registered user'
+ },
+ initial_banner => '',
+ endstate_banner => ' This problem has been fixed. ',
+ alert => 1, # we signed up for alerts before, do not unsign us
+ anonymous => 0,
+ },
+) {
+ subtest $test->{desc} => sub {
+ $mech->log_out_ok();
+
+ # clear out comments for this problem to make
+ # checking details easier later
+ ok( $_->delete, 'deleted comment ' . $_->id )
+ for $report->comments;
+
+ $mech->clear_emails_ok();
+
+ $mech->log_in_ok( $test->{fields}->{rznvy} );
+ $mech->get_ok("/report/$report_id");
+
+ my $values = $mech->visible_form_values( 'updateForm' );
+
+ is_deeply $values, $test->{initial_values}, 'initial form values';
+
+ is $mech->extract_problem_banner->{text}, $test->{initial_banner}, 'initial banner';
+
+ $mech->submit_form_ok(
+ {
+ with_fields => $test->{fields},
+ },
+ 'submit update'
+ );
+
+ is $mech->uri->path, "/report/" . $report_id, "redirected to report page";
+
+ is $mech->extract_problem_banner->{text}, $test->{endstate_banner}, 'submitted banner';
+
+ $mech->email_count_is(0);
+
+ my $results = {
+ %{ $test->{fields} },
+ %{ $test->{changed} },
+ };
+
+ my $update = $report->comments->first;
+ ok $update, 'found update';
+ is $update->text, $results->{update}, 'update text';
+ is $update->user->email, $test->{fields}->{rznvy}, 'update user';
+ is $update->state, 'confirmed', 'update confirmed';
+ is $update->anonymous, $test->{anonymous}, 'user anonymous';
+
+ my $alert =
+ FixMyStreet::App->model('DB::Alert')
+ ->find( { user => $user, alert_type => 'new_updates', confirmed => 1 } );
+
+ ok $test->{alert} ? $alert : !$alert, 'not signed up for alerts';
+ };
+}
+
+foreach my $test (
+ {
+ desc => 'logged in reporter submits update and marks problem fixed',
+ initial_values => {
+ name => 'Test User',
+ rznvy => 'test@example.com',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ },
+ fields => {
+ submit_update => 1,
+ rznvy => 'test@example.com',
+ update => 'update from owner',
+ add_alert => undef,
+ fixed => 1,
+ },
+ changed => { update => 'Update from owner' },
+ initial_banner => '',
+ alert => 1, # we signed up for alerts before, do not unsign us
+ anonymous => 0,
+ answered => 0,
+ login => 1,
+ path => '/report/update',
+ content =>
+"Thanks, glad to hear it's been fixed! Could we just ask if you have ever reported a problem to a council before?",
+ },
+ {
+ desc =>
+'logged in reporter submits update and marks problem fixed and has answered questionnaire',
+ initial_values => {
+ name => 'Test User',
+ rznvy => 'test@example.com',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ },
+ fields => {
+ submit_update => 1,
+ rznvy => 'test@example.com',
+ update => 'update from owner',
+ add_alert => undef,
+ fixed => 1,
+ },
+ changed => { update => 'Update from owner' },
+ initial_banner => '',
+ alert => 1, # we signed up for alerts before, do not unsign us
+ anonymous => 0,
+ answered => 1,
+ login => 1,
+ path => '/report/' . $report->id,
+ content => $report->title,
+ },
+ )
+{
+ subtest $test->{desc} => sub {
+
+ # double check
+ $mech->log_out_ok();
+
+ # clear out comments for this problem to make
+ # checking details easier later
+ ok( $_->delete, 'deleted comment ' . $_->id ) for $report->comments;
+
+ $report->discard_changes;
+ $report->state('confirmed');
+ $report->update;
+
+ my $questionnaire;
+ if ( $test->{answered} ) {
+ $questionnaire =
+ FixMyStreet::App->model('DB::Questionnaire')->create(
+ {
+ problem_id => $report_id,
+ ever_reported => 'y',
+ whensent => \'ms_current_timestamp()',
+ }
+ );
+
+ ok $questionnaire, 'added questionnaire';
+ }
+
+ $report->discard_changes;
+
+ $mech->clear_emails_ok();
+
+ $mech->log_in_ok( $test->{fields}->{rznvy} );
+ $mech->get_ok("/report/$report_id");
+
+ my $values = $mech->visible_form_values('updateForm');
+
+ is_deeply $values, $test->{initial_values}, 'initial form values';
+
+ is $mech->extract_problem_banner->{text}, $test->{initial_banner},
+ 'initial banner';
+
+ $mech->submit_form_ok( { with_fields => $test->{fields}, },
+ 'submit update' );
+
+ is $mech->uri->path, $test->{path}, "page after submission";
+
+ $mech->content_contains( $test->{content} );
+
+ $mech->email_count_is(0);
+
+ my $results = { %{ $test->{fields} }, %{ $test->{changed} }, };
+
+ my $update = $report->comments->first;
+ ok $update, 'found update';
+ is $update->text, $results->{update}, 'update text';
+ is $update->user->email, $test->{fields}->{rznvy}, 'update user';
+ is $update->state, 'confirmed', 'update confirmed';
+ is $update->anonymous, $test->{anonymous}, 'user anonymous';
+
+ SKIP: {
+ skip( 'not answering questionnaire', 5 ) if $questionnaire;
+
+ $mech->submit_form_ok( );
+
+ my @errors = @{ $mech->page_errors };
+ ok scalar @errors, 'displayed error messages';
+ is $errors[0], "Please say whether you've ever reported a problem to your council before", 'error message';
+
+ $mech->submit_form_ok( { with_fields => { reported => 'Yes' } } );
+
+ $mech->content_contains( 'Thank you &mdash; you can' );
+
+ $questionnaire = FixMyStreet::App->model( 'DB::Questionnaire' )->find(
+ { problem_id => $report_id }
+ );
+
+ ok $questionnaire, 'questionnaire exists';
+ ok $questionnaire->ever_reported, 'ever reported is yes';
+ };
+
+ if ($questionnaire) {
+ $questionnaire->delete;
+ ok !$questionnaire->in_storage, 'questionnaire deleted';
+ }
+ };
+}
+
+
+for my $test (
+ {
+ desc => 'reporter submits update and marks problem fixed',
+ fields => {
+ submit_update => 1,
+ name => 'Test User',
+ rznvy => 'test@example.com',
+ may_show_name => 1,
+ update => 'update from owner',
+ add_alert => undef,
+ fixed => 1,
+ },
+ changed => { update => 'Update from owner' },
+ initial_banner => '',
+ alert => 1, # we signed up for alerts before, do not unsign us
+ anonymous => 0,
+ answered => 0,
+ path => '/report/update',
+ content =>
+"Thanks, glad to hear it's been fixed! Could we just ask if you have ever reported a problem to a council before?",
+ },
+ {
+ desc =>
+'reporter submits update and marks problem fixed and has answered questionnaire',
+ fields => {
+ submit_update => 1,
+ name => 'Test User',
+ may_show_name => 1,
+ rznvy => 'test@example.com',
+ update => 'update from owner',
+ add_alert => undef,
+ fixed => 1,
+ },
+ changed => { update => 'Update from owner' },
+ initial_banner => '',
+ alert => 1, # we signed up for alerts before, do not unsign us
+ anonymous => 0,
+ answered => 1,
+ path => '/report/update',
+ content => "You have successfully confirmed your update",
+ },
+ )
+{
+ subtest $test->{desc} => sub {
+
+ # double check
+ $mech->log_out_ok();
+
+ # clear out comments for this problem to make
+ # checking details easier later
+ ok( $_->delete, 'deleted comment ' . $_->id ) for $report->comments;
+
+ $report->discard_changes;
+ $report->state('confirmed');
+ $report->update;
+
+ my $questionnaire;
+ if ( $test->{answered} ) {
+ $questionnaire =
+ FixMyStreet::App->model('DB::Questionnaire')->create(
+ {
+ problem_id => $report_id,
+ ever_reported => 'y',
+ whensent => \'ms_current_timestamp()',
+ }
+ );
+
+ ok $questionnaire, 'added questionnaire';
+ }
+
+ $report->discard_changes;
+
+ $mech->clear_emails_ok();
+
+ $mech->get_ok("/report/$report_id");
+
+ my $values = $mech->visible_form_values('updateForm');
+
+ is $mech->extract_problem_banner->{text}, $test->{initial_banner},
+ 'initial banner';
+
+ $mech->submit_form_ok( { with_fields => $test->{fields}, },
+ 'submit update' );
+
+ is $mech->uri->path, $test->{path}, "page after submission";
+
+ $mech->content_contains( 'Now check your email' );
+
+ $mech->email_count_is(1);
+
+ my $results = { %{ $test->{fields} }, %{ $test->{changed} }, };
+
+ my $update = $report->comments->first;
+ ok $update, 'found update';
+ is $update->text, $results->{update}, 'update text';
+ is $update->user->email, $test->{fields}->{rznvy}, 'update user';
+ is $update->state, 'unconfirmed', 'update confirmed';
+ is $update->anonymous, $test->{anonymous}, 'user anonymous';
+
+ my $email = $mech->get_email;
+ ok $email, "got an email";
+ like $email->body, qr/confirm the update you/i, "Correct email text";
+
+ my ( $url, $url_token ) = $email->body =~ m{(http://\S+/C/)(\S+)};
+ ok $url, "extracted confirm url '$url'";
+
+ my $token = FixMyStreet::App->model('DB::Token')->find(
+ {
+ token => $url_token,
+ scope => 'comment'
+ }
+ );
+ ok $token, 'Token found in database';
+
+ $mech->get_ok( '/C/' . $url_token );
+
+ $mech->content_contains( $test->{content} );
+
+ SKIP: {
+ skip( 'not answering questionnaire', 5 ) if $questionnaire;
+
+ $mech->submit_form_ok( );
+
+ my @errors = @{ $mech->page_errors };
+ ok scalar @errors, 'displayed error messages';
+ is $errors[0], "Please say whether you've ever reported a problem to your council before", 'error message';
+
+ $mech->submit_form_ok( { with_fields => { reported => 'Yes' } } );
+
+ $mech->content_contains( 'Thank you &mdash; you can' );
+
+ $questionnaire = FixMyStreet::App->model( 'DB::Questionnaire' )->find(
+ { problem_id => $report_id }
+ );
+
+ ok $questionnaire, 'questionnaire exists';
+ ok $questionnaire->ever_reported, 'ever reported is yes';
+ };
+
+ if ($questionnaire) {
+ $questionnaire->delete;
+ ok !$questionnaire->in_storage, 'questionnaire deleted';
+ }
+ };
+}
+
+subtest 'check have to be logged in for creator fixed questionnaire' => sub {
+ $mech->log_out_ok();
+
+ $mech->get_ok( "/questionnaire/submit?problem=$report_id&reported=Yes" );
+
+ $mech->content_contains( "I'm afraid we couldn't locate your problem in the database." )
+};
+
+subtest 'check cannot answer other user\'s creator fixed questionnaire' => sub {
+ $mech->log_out_ok();
+ $mech->log_in_ok( $user2->email );
+
+ $mech->get_ok( "/questionnaire/submit?problem=$report_id&reported=Yes" );
+
+ $mech->content_contains( "I'm afraid we couldn't locate your problem in the database." )
+};
+
+ok $comment->delete, 'deleted comment';
+$mech->delete_user('commenter@example.com');
+$mech->delete_user('test@example.com');
+done_testing();
diff --git a/t/app/controller/reports.t b/t/app/controller/reports.t
new file mode 100644
index 000000000..6cb12e20f
--- /dev/null
+++ b/t/app/controller/reports.t
@@ -0,0 +1,34 @@
+use strict;
+use warnings;
+use Test::More;
+use Test::WWW::Mechanize::Catalyst 'FixMyStreet::App';
+use mySociety::MaPit;
+
+ok( my $mech = Test::WWW::Mechanize::Catalyst->new, 'Created mech object' );
+
+# check that we can get the page
+$mech->get_ok('/reports');
+$mech->title_like(qr{Summary reports});
+$mech->content_contains('Birmingham');
+$mech->follow_link_ok( { text_regex => qr/Birmingham/ } );
+
+SKIP: {
+ skip( "Need 'emptyhomes' in ALLOWED_COBRANDS config", 8 )
+ unless FixMyStreet::App->config->{ALLOWED_COBRANDS} =~ m{emptyhomes};
+ ok $mech->host("reportemptyhomes.com"), 'change host to reportemptyhomes';
+ $mech->get_ok('/reports');
+ # EHA lacks one column the others have
+ $mech->content_lacks('state unknown');
+
+ skip( "Need 'fiksgatami' in ALLOWED_COBRANDS config", 8 )
+ unless FixMyStreet::App->config->{ALLOWED_COBRANDS} =~ m{fiksgatami};
+ mySociety::MaPit::configure('http://mapit.nuug.no/');
+ ok $mech->host("fiksgatami.no"), 'change host to fiksgatami';
+ $mech->get_ok('/reports');
+ # There should only be one Oslo
+ $mech->content_contains('Oslo');
+ $mech->content_unlike(qr{Oslo">Oslo.*Oslo}s);
+}
+
+done_testing();
+
diff --git a/t/app/controller/sample.jpg b/t/app/controller/sample.jpg
new file mode 100644
index 000000000..23198cb83
--- /dev/null
+++ b/t/app/controller/sample.jpg
Binary files differ
diff --git a/t/app/controller/tilma.t b/t/app/controller/tilma.t
new file mode 100644
index 000000000..0eb0b251e
--- /dev/null
+++ b/t/app/controller/tilma.t
@@ -0,0 +1,12 @@
+use strict;
+use warnings;
+
+use Test::More;
+use FixMyStreet::TestMech;
+
+my $mech = FixMyStreet::TestMech->new;
+
+$mech->get_ok('/tilma/tileserver/10k-full/3278-3283,1110-1115/JSON');
+is $mech->res->content_type, 'text/javascript', "got JS response";
+
+done_testing(); \ No newline at end of file
diff --git a/t/app/helpers/send_email.t b/t/app/helpers/send_email.t
new file mode 100644
index 000000000..ac7e5b5c2
--- /dev/null
+++ b/t/app/helpers/send_email.t
@@ -0,0 +1,56 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use utf8;
+
+BEGIN {
+ use FixMyStreet;
+ FixMyStreet->test_mode(1);
+}
+
+use Test::More tests => 5;
+
+use Email::Send::Test;
+use Path::Class;
+
+use_ok 'FixMyStreet::App';
+my $c = FixMyStreet::App->new(
+ {
+ request => Catalyst::Request->new(
+ {
+ base => URI->new('http://fixmystreet.com/'),
+ uri => URI->new('http://fixmystreet.com/')
+ }
+ ),
+ }
+);
+$c->setup_request();
+
+# set some values in the stash
+$c->stash->{foo} = 'bar';
+
+# clear the email queue
+Email::Send::Test->clear;
+
+# send the test email
+ok $c->send_email( 'test.txt', { to => 'test@recipient.com' } ),
+ "sent an email";
+
+# check it got templated and sent correctly
+my @emails = Email::Send::Test->emails;
+is scalar(@emails), 1, "caught one email";
+
+# Get the email, check it has a date and then strip it out
+my $email_as_string = $emails[0]->as_string;
+ok $email_as_string =~ s{\s+Date:\s+\S.*?$}{}xms, "Found and stripped out date";
+
+my $expected_email_content = file(__FILE__)->dir->file('send_email_sample.txt')->slurp;
+my $name = FixMyStreet->config('CONTACT_NAME');
+$name = "\"$name\"" if $name =~ / /;
+my $sender = $name . ' <' . FixMyStreet->config('CONTACT_EMAIL') . '>';
+$expected_email_content =~ s{CONTACT_EMAIL}{$sender};
+
+is $email_as_string,
+$expected_email_content,
+ "email is as expected";
diff --git a/t/app/helpers/send_email_sample.txt b/t/app/helpers/send_email_sample.txt
new file mode 100644
index 000000000..2fe5272cb
--- /dev/null
+++ b/t/app/helpers/send_email_sample.txt
@@ -0,0 +1,29 @@
+MIME-Version: 1.0
+Subject: test email =?utf-8?Q?=E2=98=BA?=
+Content-Type: text/plain; charset="utf-8"
+To: test@recipient.com
+Content-Transfer-Encoding: quoted-printable
+From: CONTACT_EMAIL
+
+ Hello,
+
+ This is a test email where foo: bar.
+
+ utf8: =E6=88=91=E4=BB=AC=E5=BA=94=E8=AF=A5=E8=83=BD=E5=A4=9F=E6=97=A0=
+=E7=BC=9D=E5=A4=84=E7=90=86UTF8=E7=BC=96=E7=A0=81
+
+ indented_text
+
+ long line: 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.
+
+ Yours,=20=20
+ FixMyStreet.=20=
+
+
diff --git a/t/app/load_general_config.t b/t/app/load_general_config.t
new file mode 100644
index 000000000..3855c2565
--- /dev/null
+++ b/t/app/load_general_config.t
@@ -0,0 +1,13 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+
+use_ok 'FixMyStreet::App';
+
+# GAZE_URL chosen as it is unlikely to change
+is FixMyStreet::App->config->{GAZE_URL}, #
+ 'http://gaze.mysociety.org/gaze', #
+ "check that known config param is loaded";
diff --git a/t/app/model/comment.t b/t/app/model/comment.t
new file mode 100644
index 000000000..93104c2e5
--- /dev/null
+++ b/t/app/model/comment.t
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+
+use FixMyStreet;
+use FixMyStreet::App;
+
+my $comment_rs = FixMyStreet::App->model('DB::Comment');
+
+my $comment = $comment_rs->new(
+ {
+ user_id => 1,
+ problem_id => 1,
+ text => '',
+ state => 'confirmed',
+ anonymous => 0,
+ mark_fixed => 0,
+ cobrand => 'default',
+ cobrand_data => '',
+ }
+);
+
+is $comment->confirmed_local, undef, 'inflating null confirmed ok';
+is $comment->created_local, undef, 'inflating null confirmed ok';
diff --git a/t/app/model/db.t b/t/app/model/db.t
new file mode 100644
index 000000000..bebd68f0b
--- /dev/null
+++ b/t/app/model/db.t
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+use_ok 'FixMyStreet::App::Model::DB';
+
+done_testing();
diff --git a/t/app/model/problem.t b/t/app/model/problem.t
new file mode 100644
index 000000000..1b8804fce
--- /dev/null
+++ b/t/app/model/problem.t
@@ -0,0 +1,155 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+use FixMyStreet;
+use FixMyStreet::App;
+use mySociety::Locale;
+
+mySociety::Locale::gettext_domain('FixMyStreet');
+
+my $problem_rs = FixMyStreet::App->model('DB::Problem');
+
+my $problem = $problem_rs->new(
+ {
+ postcode => 'EH99 1SP',
+ latitude => 1,
+ longitude => 1,
+ areas => 1,
+ title => '',
+ detail => '',
+ used_map => 1,
+ user_id => 1,
+ name => '',
+ state => 'confirmed',
+ service => '',
+ cobrand => 'default',
+ cobrand_data => '',
+ }
+);
+
+is $problem->confirmed_local, undef, 'inflating null confirmed ok';
+is $problem->whensent_local, undef, 'inflating null confirmed ok';
+is $problem->lastupdate_local, undef, 'inflating null confirmed ok';
+is $problem->created_local, undef, 'inflating null confirmed ok';
+
+for my $test (
+ {
+ desc => 'more or less empty problem',
+ changed => {},
+ errors => {
+ title => 'Please enter a subject',
+ detail => 'Please enter some details',
+ council => 'No council selected',
+ name => 'Please enter your name',
+ }
+ },
+ {
+ desc => 'name too short',
+ changed => {
+ name => 'xx',
+ },
+ errors => {
+ title => 'Please enter a subject',
+ detail => 'Please enter some details',
+ council => 'No council selected',
+ name => 'Please enter your full name, councils need this information - if you do not wish your name to be shown on the site, untick the box',
+ }
+ },
+ {
+ desc => 'name is anonymous',
+ changed => {
+ name => 'anonymous',
+ },
+ errors => {
+ title => 'Please enter a subject',
+ detail => 'Please enter some details',
+ council => 'No council selected',
+ name => 'Please enter your full name, councils need this information - if you do not wish your name to be shown on the site, untick the box',
+ }
+ },
+ {
+ desc => 'correct name',
+ changed => {
+ name => 'A User',
+ },
+ errors => {
+ title => 'Please enter a subject',
+ detail => 'Please enter some details',
+ council => 'No council selected',
+ }
+ },
+ {
+ desc => 'correct title',
+ changed => {
+ title => 'A Title',
+ },
+ errors => {
+ detail => 'Please enter some details',
+ council => 'No council selected',
+ }
+ },
+ {
+ desc => 'correct detail',
+ changed => {
+ detail => 'Some information about the problem',
+ },
+ errors => {
+ council => 'No council selected',
+ }
+ },
+ {
+ desc => 'incorrectly formatted council',
+ changed => {
+ council => 'my council',
+ },
+ errors => {
+ council => 'No council selected',
+ }
+ },
+ {
+ desc => 'correctly formatted council',
+ changed => {
+ council => '1001',
+ },
+ errors => {
+ }
+ },
+ {
+ desc => 'bad category',
+ changed => {
+ category => '-- Pick a category --',
+ },
+ errors => {
+ category => 'Please choose a category',
+ }
+ },
+ {
+ desc => 'bad category',
+ changed => {
+ category => '-- Pick a property type --',
+ },
+ errors => {
+ category => 'Please choose a property type',
+ }
+ },
+ {
+ desc => 'correct category',
+ changed => {
+ category => 'Horse!',
+ },
+ errors => {
+ }
+ },
+) {
+ $problem->$_( $test->{changed}->{$_} ) for keys %{$test->{changed}};
+
+ subtest $test->{desc} => sub {
+ is_deeply $problem->check_for_errors, $test->{errors}, 'check for errors';
+ };
+}
+
+done_testing();
diff --git a/t/app/model/token.t b/t/app/model/token.t
new file mode 100644
index 000000000..12945975e
--- /dev/null
+++ b/t/app/model/token.t
@@ -0,0 +1,96 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 45;
+
+use FixMyStreet;
+use FixMyStreet::App;
+use mySociety::AuthToken;
+use mySociety::DBHandle 'dbh';
+
+# set things up so that code using mySociety::DBHandle is happy
+FixMyStreet->configure_mysociety_dbhandle();
+
+# NOTE - remember that you need to explicitly dbh()->commit after making
+# database changes with the mySociety::* modules.
+
+# create a token using DBIC and check we can read it using AuthToken, and vice
+# versa
+
+my %tests = (
+ nested_hash => { foo => 'bar', and => [ 'baz', 'bundy' ] },
+ array => [ 'foo', 'bar' ],
+ scalar => 123,
+);
+
+my $token_rs = FixMyStreet::App->model('DB::Token');
+
+# create using DBIC
+foreach my $test_data_name ( sort keys %tests ) {
+ my $test_data = $tests{$test_data_name};
+
+ pass "--- testing DBIC create using '$test_data_name'";
+
+ my $dbic_token =
+ $token_rs->create( { scope => 'testing', data => $test_data } );
+ my $token = $dbic_token->token;
+ ok $token, "stored token '$token'";
+
+ is_deeply $dbic_token->data, $test_data, "data stored correctly using DBIC";
+
+ # read back using DBIC
+ is_deeply $token_rs->find( { token => $token, scope => 'testing' } )->data,
+ $test_data,
+ "data read back correctly with DBIC";
+
+ # read back using mySociety::AuthToken
+ is_deeply mySociety::AuthToken::retrieve( 'testing', $token ),
+ $test_data, "data read back correctly with m::AT";
+
+ # delete token
+ ok $dbic_token->delete, "delete token";
+
+ is $token_rs->find( { token => $token, scope => 'testing' } ),
+ undef,
+ "token gone for DBIC";
+
+ # read back using mySociety::AuthToken
+ is mySociety::AuthToken::retrieve( 'testing', $token ),
+ undef, "token gone with m::AT";
+
+}
+
+# create using m::AT
+foreach my $test_data_name ( sort keys %tests ) {
+ my $test_data = $tests{$test_data_name};
+
+ pass "--- testing m::AT create using '$test_data_name'";
+
+ my $token = mySociety::AuthToken::store( 'testing', $test_data );
+ dbh->commit();
+ ok $token, "stored token '$token'";
+
+ # read back using DBIC
+ is_deeply $token_rs->find( { token => $token, scope => 'testing' } )->data,
+ $test_data,
+ "data read back correctly with DBIC";
+
+ # read back using mySociety::AuthToken
+ is_deeply mySociety::AuthToken::retrieve( 'testing', $token ),
+ $test_data, "data read back correctly with m::AT";
+
+ # delete token
+ ok mySociety::AuthToken::destroy( 'testing', $token ), "destroy token";
+ dbh->commit();
+
+ is $token_rs->find( { token => $token, scope => 'testing' } ),
+ undef,
+ "token gone for DBIC";
+
+ # read back using mySociety::AuthToken
+ is mySociety::AuthToken::retrieve( 'testing', $token ),
+ undef, "token gone with m::AT";
+
+}
diff --git a/t/app/uri_for.t b/t/app/uri_for.t
new file mode 100644
index 000000000..51a6e8a0e
--- /dev/null
+++ b/t/app/uri_for.t
@@ -0,0 +1,108 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+# FIXME Should this be here? A better way? uri_for varies by map.
+use Test::WWW::Mechanize::Catalyst 'FixMyStreet::App';
+FixMyStreet::Map::set_map_class();
+
+# structure of these tests borrowed from '/t/aggregate/unit_core_uri_for.t'
+
+use strict;
+use warnings;
+use URI;
+
+use_ok('FixMyStreet::App');
+
+my $fms_c = FixMyStreet::App->new(
+ {
+ request => Catalyst::Request->new(
+ {
+ base => URI->new('http://www.fixmystreet.com/'),
+ uri => URI->new('http://www.fixmystreet.com/test_namespace')
+ }
+ ),
+ namespace => 'test_namespace',
+ }
+);
+
+my $fgm_c = FixMyStreet::App->new(
+ {
+ request => Catalyst::Request->new(
+ {
+ base => URI->new('http://www.fiksgatami.no/'),
+ uri => URI->new('http://www.fiksgatami.no/test_namespace')
+ }
+ ),
+ namespace => 'test_namespace',
+ }
+);
+
+my $reh_en_c = FixMyStreet::App->new(
+ {
+ request => Catalyst::Request->new(
+ {
+ base => URI->new('http://reportemptyhomes.com/'),
+ uri => URI->new('http://reportemptyhomes.com/test_namespace')
+ }
+ ),
+ namespace => 'test_namespace',
+ }
+);
+$reh_en_c->setup_request();
+
+
+is(
+ $fms_c->uri_for('/bar/baz') . "",
+ 'http://www.fixmystreet.com/bar/baz',
+ 'URI for absolute path'
+);
+
+is(
+ $fms_c->uri_for('') . "",
+ 'http://www.fixmystreet.com/test_namespace',
+ 'URI for namespace'
+);
+
+is(
+ $fms_c->uri_for( '/bar/baz', 'boing', { foo => 'bar', } ) . "",
+ 'http://www.fixmystreet.com/bar/baz/boing?foo=bar',
+ 'URI with query'
+);
+
+# fiksgatami
+is(
+ $fgm_c->uri_for( '/foo', { lat => 1.23, } ) . "",
+ 'http://www.fiksgatami.no/foo?lat=1.23&zoom=3',
+ 'FiksGataMi url with lat not zoom'
+);
+
+like(
+ $reh_en_c->uri_for_email( '/foo' ),
+ qr{^http://en.},
+ 'adds en to retain language'
+);
+
+# instantiate this here otherwise sets locale to cy and breaks test
+# above
+my $reh_cy_c = FixMyStreet::App->new(
+ {
+ request => Catalyst::Request->new(
+ {
+ base => URI->new('http://cy.reportemptyhomes.com/'),
+ uri => URI->new('http://cy.reportemptyhomes.com/test_namespace')
+ }
+ ),
+ namespace => 'test_namespace',
+ }
+);
+$reh_cy_c->setup_request();
+
+like(
+ $reh_cy_c->uri_for_email( '/foo' ),
+ qr{^http://cy.},
+ 'retains language'
+);
+
+done_testing();
diff --git a/t/app/view/email.t b/t/app/view/email.t
new file mode 100644
index 000000000..4d7bbe8ff
--- /dev/null
+++ b/t/app/view/email.t
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+BEGIN { use_ok 'FixMyStreet::App::View::Email' }
+
+done_testing();
diff --git a/t/app/view/web.t b/t/app/view/web.t
new file mode 100644
index 000000000..0f49b986b
--- /dev/null
+++ b/t/app/view/web.t
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+use Test::More;
+
+BEGIN { use_ok 'FixMyStreet::App::View::Web' }
+
+done_testing();
diff --git a/t/cobrand/loading.t b/t/cobrand/loading.t
new file mode 100644
index 000000000..405ef4761
--- /dev/null
+++ b/t/cobrand/loading.t
@@ -0,0 +1,71 @@
+use strict;
+use warnings;
+
+use Test::More;
+use Sub::Override;
+
+use FixMyStreet;
+
+use_ok 'FixMyStreet::Cobrand';
+
+# check that the allowed cobrands is correctly loaded from config
+{
+ my $allowed = FixMyStreet::Cobrand->get_allowed_cobrands;
+ ok $allowed, "got the allowed_cobrands";
+ isa_ok $allowed, "ARRAY";
+ cmp_ok scalar @$allowed, '>', 1, "got more than one";
+ is join( '|', @$allowed ), FixMyStreet->config('ALLOWED_COBRANDS'),
+ "matches config value";
+}
+
+# fake the allowed cobrands for testing
+my $override = Sub::Override->new( #
+ 'FixMyStreet::Cobrand::get_allowed_cobrands' =>
+ sub { return ['emptyhomes'] }
+);
+is_deeply FixMyStreet::Cobrand->get_allowed_cobrands, ['emptyhomes'],
+ 'overidden get_allowed_cobrands';
+
+sub run_host_tests {
+ my %host_tests = @_;
+ for my $host ( sort keys %host_tests ) {
+ is FixMyStreet::Cobrand->get_class_for_host($host),
+ "FixMyStreet::Cobrand::$host_tests{$host}",
+ "does $host -> F::C::$host_tests{$host}";
+ }
+}
+
+# get the cobrand class by host
+run_host_tests(
+ 'www.fixmystreet.com' => 'Default',
+ 'reportemptyhomes.com' => 'EmptyHomes',
+ 'barnet.fixmystreet.com' => 'Default', # not in the allowed_cobrands list
+ 'some.odd.site.com' => 'Default',
+);
+
+# now enable barnet too and check that it works
+$override->replace( #
+ 'FixMyStreet::Cobrand::get_allowed_cobrands' =>
+ sub { return [ 'emptyhomes', 'barnet' ] }
+);
+
+# get the cobrand class by host
+run_host_tests(
+ 'www.fixmystreet.com' => 'Default',
+ 'reportemptyhomes.com' => 'EmptyHomes',
+ 'barnet.fixmystreet.com' => 'Barnet', # found now it is in allowed_cobrands
+ 'some.odd.site.com' => 'Default',
+);
+
+# check that the moniker works as expected both on class and object.
+is FixMyStreet::Cobrand::EmptyHomes->moniker, 'emptyhomes',
+ 'class->moniker works';
+is FixMyStreet::Cobrand::EmptyHomes->new->moniker, 'emptyhomes',
+ 'object->moniker works';
+
+# check is_default works
+ok FixMyStreet::Cobrand::Default->is_default, '::Default is default';
+ok !FixMyStreet::Cobrand::EmptyHomes->is_default, '::Emptyhomes is not default';
+
+# all done
+done_testing();
diff --git a/t/fixmystreet.t b/t/fixmystreet.t
new file mode 100644
index 000000000..d7f00b047
--- /dev/null
+++ b/t/fixmystreet.t
@@ -0,0 +1,37 @@
+use strict;
+use warnings;
+use Path::Class;
+
+use Test::More;
+use Test::Exception;
+
+use_ok 'FixMyStreet';
+
+# check that the path_to works
+my $file_path = file(__FILE__)->absolute->stringify;
+my $path_to_path = FixMyStreet->path_to('t/fixmystreet.t');
+
+isa_ok $path_to_path, 'Path::Class::File';
+ok $path_to_path->is_absolute, "path is absolute";
+is "$path_to_path", $file_path, "got $file_path";
+
+# check that the config gets loaded and is immutable
+my $config = FixMyStreet->config;
+isa_ok $config, 'HASH';
+is $config->{GAZE_URL}, 'http://gaze.mysociety.org/gaze',
+ "got GAZE_URL correctly";
+throws_ok(
+ sub { $config->{GAZE_URL} = 'some other value'; },
+ qr/Modification of a read-only value attempted/,
+ 'attempt to change config caught'
+);
+is $config->{GAZE_URL}, 'http://gaze.mysociety.org/gaze', "GAZE_URL unchanged";
+
+# check that we can get the value by key as well
+is FixMyStreet->config('GAZE_URL'), 'http://gaze.mysociety.org/gaze',
+ "GAZE_URL correct when got by key";
+is FixMyStreet->config('BAD_KEY_DOES_NOT_EXIST'), undef, "config miss is undef";
+
+# all done
+done_testing();
+
diff --git a/t/i18n.t b/t/i18n.t
new file mode 100644
index 000000000..a5b68782b
--- /dev/null
+++ b/t/i18n.t
@@ -0,0 +1,109 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+use FixMyStreet;
+use mySociety::Locale;
+use Encode;
+use Data::Dumper;
+use Sort::Key qw(keysort);
+use POSIX 'strcoll';
+local $Data::Dumper::Sortkeys = 1;
+use utf8;
+
+# check that the mo files have been generated
+die "You need to run 'commonlib/bin/gettext-makemo --quiet FixMyStreet' "
+ . "to generate the *.mo files needed."
+ unless -e FixMyStreet->path_to(
+ 'locale/cy_GB.UTF-8/LC_MESSAGES/FixMyStreet-EmptyHomes.mo');
+
+# Example strings
+my $english = "Sorry! Something's gone wrong.";
+my $welsh = "Ymddiheuriadau! Mae rhywbeth wedi mynd o'i le.";
+
+# set english as the language
+mySociety::Locale::negotiate_language( #
+ 'en-gb,English,en_GB|cy,Cymraeg,cy_GB', 'en_GB'
+);
+
+mySociety::Locale::gettext_domain( 'FixMyStreet-EmptyHomes', 1 );
+mySociety::Locale::change();
+is _($english), $english, "english to english";
+
+# set to welsh and check for translation
+mySociety::Locale::change('cy');
+is _($english), $welsh, "english to welsh";
+
+# check that being in a deep directory does not confuse the code
+chdir FixMyStreet->path_to('t/app/controller') . '';
+mySociety::Locale::gettext_domain( 'FixMyStreet-EmptyHomes', 1,
+ FixMyStreet->path_to('locale')->stringify );
+mySociety::Locale::change('cy');
+is _($english), $welsh, "english to welsh (deep directory)";
+
+# test that sorting works as expected in the right circumstances...
+my @random_sorted = qw( Å Z Ø A );
+my @EN_sorted = qw( A Å Ø Z );
+my @NO_sorted = qw( A Z Ø Å );
+my @default_sorted = qw( A Z Å Ø );
+
+sub utf8_diag {
+ diag encode_utf8( Dumper(@_) );
+}
+
+{
+
+ mySociety::Locale::negotiate_language( #
+ 'en-gb,English,en_GB|cy,Cymraeg,cy_GB', 'en_GB'
+ );
+ mySociety::Locale::change();
+
+ no locale;
+
+ is_deeply( [ sort @random_sorted ],
+ \@default_sorted, "sort correctly with no locale" );
+
+ is_deeply( [ keysort { $_ } @random_sorted ],
+ \@default_sorted, "keysort correctly with no locale" );
+
+ # Note - this obeys the locale
+ is_deeply( [ sort { strcoll( $a, $b ) } @random_sorted ],
+ \@EN_sorted, "sort strcoll correctly with no locale (to 'en_GB')" );
+}
+
+{
+ mySociety::Locale::negotiate_language( #
+ 'en-gb,English,en_GB|cy,Cymraeg,cy_GB', 'en_GB'
+ );
+ mySociety::Locale::change();
+ use locale;
+
+ is_deeply( [ sort @random_sorted ],
+ \@EN_sorted, "sort correctly with use locale 'en_GB'" );
+
+ # is_deeply( [ keysort { $_ } @random_sorted ],
+ # \@EN_sorted, "keysort correctly with use locale 'en_GB'" );
+
+ is_deeply( [ sort { strcoll( $a, $b ) } @random_sorted ],
+ \@EN_sorted, "sort strcoll correctly with use locale 'en_GB'" );
+}
+
+{
+ mySociety::Locale::negotiate_language( #
+ 'nb-no,Norwegian,nb_NO', 'nb_NO'
+ );
+ mySociety::Locale::change();
+ use locale;
+
+ is_deeply( [ sort @random_sorted ],
+ \@NO_sorted, "sort correctly with use locale 'nb_NO'" );
+
+ # is_deeply( [ keysort { $_ } @random_sorted ],
+ # \@NO_sorted, "keysort correctly with use locale 'nb_NO'" );
+
+ is_deeply( [ sort { strcoll( $a, $b ) } @random_sorted ],
+ \@NO_sorted, "sort strcoll correctly with use locale 'nb_NO'" );
+}
+
+done_testing();
diff --git a/t/templates/mysite/test-header b/t/templates/mysite/test-header
deleted file mode 100644
index 83d959d3d..000000000
--- a/t/templates/mysite/test-header
+++ /dev/null
@@ -1 +0,0 @@
-My test header template
diff --git a/templates/emails/alert-confirm b/templates/email/default/alert-confirm.txt
index bb6288119..fae3f2963 100644
--- a/templates/emails/alert-confirm
+++ b/templates/email/default/alert-confirm.txt
@@ -5,7 +5,7 @@ Hi,
Please click on the link below to confirm the alert you just
asked to subscribe to on FixMyStreet:
- <?=$values['url']?>
+ [% token_url %]
If you can't click the link, please copy and paste it to the
address bar of your web browser.
diff --git a/templates/emails/alert-problem-area b/templates/email/default/alert-problem-area.txt
index 8c41aaf5e..8c41aaf5e 100644
--- a/templates/emails/alert-problem-area
+++ b/templates/email/default/alert-problem-area.txt
diff --git a/templates/emails/alert-problem-council b/templates/email/default/alert-problem-council.txt
index 572e057a6..572e057a6 100644
--- a/templates/emails/alert-problem-council
+++ b/templates/email/default/alert-problem-council.txt
diff --git a/templates/emails/alert-problem-nearby b/templates/email/default/alert-problem-nearby.txt
index 0bf8483a7..0bf8483a7 100644
--- a/templates/emails/alert-problem-nearby
+++ b/templates/email/default/alert-problem-nearby.txt
diff --git a/templates/emails/alert-problem-ward b/templates/email/default/alert-problem-ward.txt
index efcf15993..efcf15993 100644
--- a/templates/emails/alert-problem-ward
+++ b/templates/email/default/alert-problem-ward.txt
diff --git a/templates/emails/alert-problem b/templates/email/default/alert-problem.txt
index 8369b8e28..8369b8e28 100644
--- a/templates/emails/alert-problem
+++ b/templates/email/default/alert-problem.txt
diff --git a/templates/emails/alert-update b/templates/email/default/alert-update.txt
index ac0ddd50a..ac0ddd50a 100644
--- a/templates/emails/alert-update
+++ b/templates/email/default/alert-update.txt
diff --git a/templates/email/default/contact.txt b/templates/email/default/contact.txt
new file mode 100644
index 000000000..59a778ad5
--- /dev/null
+++ b/templates/email/default/contact.txt
@@ -0,0 +1,5 @@
+Subject: FMS message: [% subject %]
+
+[% message %]
+
+[ Sent by contact.cgi on [% host %]. IP address [% ip %] ]
diff --git a/templates/email/default/login.txt b/templates/email/default/login.txt
new file mode 100644
index 000000000..c873e82af
--- /dev/null
+++ b/templates/email/default/login.txt
@@ -0,0 +1,12 @@
+Subject: [% loc('Your FixMyStreet.com account details') %]
+
+Please click on the link below to confirm your email address. Then you will be able to view your problem reports.
+
+[% c.uri_for( '/auth/token', token ) %]
+
+We will never give away or sell your email address to anyone else without your permission.
+
+Yours,
+ the FixMyStreet.com team
+
+
diff --git a/templates/emails/partial b/templates/email/default/partial.txt
index d754744b5..279d76ea0 100644
--- a/templates/emails/partial
+++ b/templates/email/default/partial.txt
@@ -1,12 +1,12 @@
Subject: Confirm your report on FixMyStreet
-Hi<?=$values['name']?>,
+Hi [% report.name || report.email %],
To confirm the report you have uploaded to FixMyStreet via
-<?=$values['service']?>, and to check or add any details,
+[% report.service %], and to check or add any details,
please visit the following URL:
-<?=$values['url']?>
+[% token_url %]
Thanks!
diff --git a/templates/emails/problem-confirm b/templates/email/default/problem-confirm.txt
index 2a00e54a5..e16c90037 100644
--- a/templates/emails/problem-confirm
+++ b/templates/email/default/problem-confirm.txt
@@ -1,20 +1,20 @@
Subject: Confirm your problem on FixMyStreet
-Hi <?=$values['name']?>,
+Hi [% report.user.name %],
Please click on the link below to confirm the problem you just
added to FixMyStreet:
-<?=$values['url']?>
+[% token_url %]
If your email program does not let you click on this link,
copy and paste it into your web browser and press return.
Your problem had the title:
-<?=$values['title']?>
+[% report.title %]
And details:
-<?=$values['detail']?>
+[% report.detail %]
Yours,
The FixMyStreet team
diff --git a/templates/emails/questionnaire b/templates/email/default/questionnaire.txt
index 4be8eeaa4..4be8eeaa4 100644
--- a/templates/emails/questionnaire
+++ b/templates/email/default/questionnaire.txt
diff --git a/templates/emails/reply-autoresponse b/templates/email/default/reply-autoresponse
index 672c1f3c9..672c1f3c9 100644
--- a/templates/emails/reply-autoresponse
+++ b/templates/email/default/reply-autoresponse
diff --git a/templates/emails/submit-brent b/templates/email/default/submit-brent.txt
index f3a9e5bb8..fcddcc8f1 100644
--- a/templates/emails/submit-brent
+++ b/templates/email/default/submit-brent.txt
@@ -25,16 +25,21 @@ Details: <?=$values['detail']?>
Longitude: <?=$values['longitude']?>
-<?=$values['closest_address_machine']?>----------
+<?=$values['closest_address']?>----------
Replies to this email will go to the user who submitted the problem.
Yours,
The FixMyStreet team
-[ This message was sent via FixMyStreet, a project of UKCOD, registered charity
+This message was sent via FixMyStreet, a project of UKCOD, registered charity
number 1076346. If there is a more appropriate email address for messages about
<?=$values['category_footer']?>, please let us know by visiting <http://www.fixmystreet.com/contact>.
This will help improve the service for local people. We
-also welcome any other feedback you may have. ]
+also welcome any other feedback you may have.
+
+FixMyStreet is now available for full integration into council
+websites, making life easier for both you and your residents.
+Read more here:
+http://www.mysociety.org/fixmystreet-for-local-council-websites/
diff --git a/templates/emails/submit-council b/templates/email/default/submit.txt
index 21c89119d..2542e786b 100644
--- a/templates/emails/submit-council
+++ b/templates/email/default/submit.txt
@@ -32,9 +32,14 @@ Replies to this email will go to the user who submitted the problem.
Yours,
The FixMyStreet team
-[ This message was sent via FixMyStreet, a project of UKCOD, registered charity
+This message was sent via FixMyStreet, a project of UKCOD, registered charity
number 1076346. If there is a more appropriate email address for messages about
<?=$values['category_footer']?>, please let us know by visiting <http://www.fixmystreet.com/contact>.
This will help improve the service for local people. We
-also welcome any other feedback you may have. ]
+also welcome any other feedback you may have.
+
+FixMyStreet is now available for full integration into council
+websites, making life easier for both you and your residents.
+Read more here:
+http://www.mysociety.org/fixmystreet-for-local-council-websites/
diff --git a/templates/email/default/test.txt b/templates/email/default/test.txt
new file mode 100644
index 000000000..bfa2c1dd3
--- /dev/null
+++ b/templates/email/default/test.txt
@@ -0,0 +1,15 @@
+Subject: test email ☺
+From: bad-sender@duff.com
+
+Hello,
+
+This is a test email where foo: [% foo %].
+
+utf8: 我们应该能够无ç¼å¤„ç†UTF8ç¼–ç 
+
+ indented_text
+
+long line: 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.
+
+Yours,
+FixMyStreet.
diff --git a/templates/email/default/update-confirm.txt b/templates/email/default/update-confirm.txt
new file mode 100644
index 000000000..9a0db3029
--- /dev/null
+++ b/templates/email/default/update-confirm.txt
@@ -0,0 +1,18 @@
+Subject: Confirm your update on FixMyStreet
+
+Hi [% update.user.name %],
+
+Please click on the link below to confirm the update you just
+wrote:
+
+[% token_url %]
+
+If your email program does not let you click on this link,
+copy and paste it into your web browser and press return.
+
+Your update reads:
+
+[% update.text %]
+
+Yours,
+The FixMyStreet team
diff --git a/templates/email/emptyhomes/cy/alert-confirm.txt b/templates/email/emptyhomes/cy/alert-confirm.txt
new file mode 100644
index 000000000..9be79a952
--- /dev/null
+++ b/templates/email/emptyhomes/cy/alert-confirm.txt
@@ -0,0 +1,15 @@
+Subject: Cadarnhau eich rhybudd ar reportemptyhomes.com
+
+Helo,
+
+Cliciwch ar y ddolen isod i gadarnhau'r rhybudd yr ydych newydd
+ofyn am danysgrifio iddo ar Asiantaeth Tai Gwag:
+
+ [% token_url %]
+
+Os na allwch glicio ar y ddolen, dylech ei chopïo a'i gludo ym
+mar cyfeiriad eich porwr gwe.
+
+Yn gywir,
+Tîm yr Asiantaeth Tai Gwag
+
diff --git a/templates/email/emptyhomes/cy/alert-problem-area.txt b/templates/email/emptyhomes/cy/alert-problem-area.txt
new file mode 100644
index 000000000..c7712f62f
--- /dev/null
+++ b/templates/email/emptyhomes/cy/alert-problem-area.txt
@@ -0,0 +1,13 @@
+Subject: Adroddiadau newydd am eiddo gwag yn ardal <?=$values['area_name']?> ar reportemptyhomes.com
+
+Mae'r eiddo gwag canlynol wedi cael eu hychwanegu yn ardal
+<?=$values['area_name']?>:
+
+<?=$values['data']?>
+
+Yn gywir,
+reportemptyhomes.com
+
+I roi'r gorau i dderbyn negeseuon e-bost pan fydd eiddo newydd yn ymddangos yn ardal
+<?=$values['area_name']?>, dilynwch y ddolen hon:
+<?=$values['unsubscribe_url']?>
diff --git a/templates/email/emptyhomes/cy/alert-problem-council.txt b/templates/email/emptyhomes/cy/alert-problem-council.txt
new file mode 100644
index 000000000..678e3fa9f
--- /dev/null
+++ b/templates/email/emptyhomes/cy/alert-problem-council.txt
@@ -0,0 +1,12 @@
+Subject: Adroddiadau newydd am eiddo gwag a hysbyswyd i ardal <?=$values['area_name']?> ar reportemptyhomes.com
+
+Mae'r eiddo gwag canlynol wedi cael eu hysbysu i ardal <?=$values['area_name']?>:
+
+<?=$values['data']?>
+
+Yn gywir,
+reportemptyhomes.com
+
+I roi'r gorau i dderbyn negeseuon e-bost pan gaiff eiddo gwag newydd eu hysbysu i ardal
+<?=$values['area_name']?>, dilynwch y ddolen hon:
+<?=$values['unsubscribe_url']?>
diff --git a/templates/email/emptyhomes/cy/alert-problem-nearby.txt b/templates/email/emptyhomes/cy/alert-problem-nearby.txt
new file mode 100644
index 000000000..b72d68979
--- /dev/null
+++ b/templates/email/emptyhomes/cy/alert-problem-nearby.txt
@@ -0,0 +1,11 @@
+Subject: Eiddo gwag cyfagos newydd ar reportemptyhomes.com
+
+Mae'r eiddo gwag cyfagos newydd wedi cael eu hychwanegu:
+
+<?=$values['data']?>
+
+Yn gywir,
+reportemptyhomes.com
+
+I roi'r gorau i dderbyn negeseuon e-bost pan fydd eiddo gerllaw,
+dilynwch y ddolen hon: <?=$values['unsubscribe_url']?>
diff --git a/templates/email/emptyhomes/cy/alert-problem-ward.txt b/templates/email/emptyhomes/cy/alert-problem-ward.txt
new file mode 100644
index 000000000..603c55fa0
--- /dev/null
+++ b/templates/email/emptyhomes/cy/alert-problem-ward.txt
@@ -0,0 +1,13 @@
+Subject: Adroddiadau newydd am eiddo gwag a hysbyswyd i ardal <?=$values['area_name']?>, yn ward <?=$values['ward_name']?> ar reportemptyhomes.com
+
+Mae'r eiddo gwag canlynol wedi cael eu hysbysu i ardal <?=$values['area_name']?>,
+o fewn ward <?=$values['ward_name']?>:
+
+<?=$values['data']?>
+
+Yn gywir,
+reportemptyhomes.com
+
+I roi'r gorau i dderbyn negeseuon e-bost pan gaiff eiddo newydd eu hysbysu i ardal
+<?=$values['area_name']?>, o fewn ward <?=$values['ward_name']?>,
+dilynwch y ddolen hon: <?=$values['unsubscribe_url']?>
diff --git a/templates/email/emptyhomes/cy/alert-problem.txt b/templates/email/emptyhomes/cy/alert-problem.txt
new file mode 100644
index 000000000..a2c56cf52
--- /dev/null
+++ b/templates/email/emptyhomes/cy/alert-problem.txt
@@ -0,0 +1,11 @@
+Subject: Adroddiadau newydd am eiddo gwag ar reportemptyhomes.com
+
+Mae'r eiddo gwag canlynol wedi cael eu hychwanegu:
+
+<?=$values['data']?>
+
+Yn gywir,
+reportemptyhomes.com
+
+I roi'r gorau i dderbyn negeseuon e-bost pan fydd eiddo newydd yn ymddangos,
+dilynwch y ddolen hon: <?=$values['unsubscribe_url']?>
diff --git a/templates/email/emptyhomes/cy/alert-update.txt b/templates/email/emptyhomes/cy/alert-update.txt
new file mode 100644
index 000000000..9a80d55c5
--- /dev/null
+++ b/templates/email/emptyhomes/cy/alert-update.txt
@@ -0,0 +1,13 @@
+Subject: Diweddariadau newydd am eiddo gwag - '<?=$values['title']?>'
+
+Mae'r diweddariadau canlynol wedi cael eu gadael am yr eiddo gwag hwn:
+
+<?=$values['data']?>
+
+Gweld y diweddariadau hyn neu eu hateb: <?=$values['problem_url']?>
+
+Yn gywir,
+reportemptyhomes.com
+
+I roi'r gorau i dderbyn negeseuon e-bost pan fydd diweddariadau newydd yn ymddangos am yr eiddo hwn,
+dilynwch y ddolen hon: <?=$values['unsubscribe_url']?>
diff --git a/templates/email/emptyhomes/cy/partial.txt b/templates/email/emptyhomes/cy/partial.txt
new file mode 100644
index 000000000..0692e2548
--- /dev/null
+++ b/templates/email/emptyhomes/cy/partial.txt
@@ -0,0 +1,16 @@
+Subject: Eich adroddiad newydd ar reportemptyhomes.com
+
+Helo[% report.name || report.email %],
+
+Rydym wedi storio'r adroddiad y gwnaethoch ei lwytho i'r Asiantaeth Tai Gwag
+drwy gyfrwng
+[% report.service %]. I gadarnhau'r manylion sydd gennym,
+ac i ychwanegu atynt, ewch i'r URL canlynol:
+
+[% token_url %]
+
+Yna, gallwn anfon eich adroddiad at y cyngor. Diolch!
+
+Yn gywir,
+Tîm yr Asiantaeth Tai Gwag
+
diff --git a/templates/email/emptyhomes/cy/problem-confirm.txt b/templates/email/emptyhomes/cy/problem-confirm.txt
new file mode 100644
index 000000000..a9f1b021d
--- /dev/null
+++ b/templates/email/emptyhomes/cy/problem-confirm.txt
@@ -0,0 +1,21 @@
+Subject: Cadarnhau eich adroddiad am eiddo gwag
+
+Helo [% report.user.name %],
+
+Cliciwch ar y ddolen isod i gadarnhau'r adroddiad am eiddo gwag
+yr ydych newydd ei ychwanegu at y safle:
+
+[% token_url %]
+
+Os nad yw eich rhaglen e-bost yn gadael i chi glicio ar y ddolen hon,
+dylech ei chopïo a'i gludo i'ch porwr gwe a phwyso'r fysell 'return'.
+
+Roedd gan eich adroddiad y pwnc:
+[% report.title %]
+
+A'r manylion:
+[% report.detail %]
+
+Yn gywir,
+reportemptyhomes.com
+
diff --git a/templates/email/emptyhomes/cy/questionnaire-26weeks.txt b/templates/email/emptyhomes/cy/questionnaire-26weeks.txt
new file mode 100644
index 000000000..b2b40ac80
--- /dev/null
+++ b/templates/email/emptyhomes/cy/questionnaire-26weeks.txt
@@ -0,0 +1,25 @@
+Subject: Holiadur ar eich adroddiad am eiddo gwag
+
+Helo <?=$values['name']?>,
+
+Chwe mis yn ôl, fe wnaethoch adrodd am eiddo gwag ar ReportEmptyHomes.com gan roi'r
+manylion sydd wedi'u cynnwys ar ddiwedd y neges e-bost hon. Er mwyn cadw'n safle'n gyfredol
+ac yn berthnasol, byddwn yn ddiolchgar petaech yn llenwi'r holiadur byr hwn er mwyn
+dweud wrthom ni beth sydd wedi digwydd:
+
+ <?=$values['url']?>
+
+Peidiwch ag ateb y neges hon; mae blwch sylwadau cyhoeddus
+ar yr holiadur.
+
+Yn gywir,
+reportemptyhomes.com
+
+Roedd eich adroddiad fel a ganlyn:
+
+<?=$values['title']?>
+
+Math o eiddo: <?=$values['category']?>
+
+<?=$values['detail']?>
+
diff --git a/templates/email/emptyhomes/cy/questionnaire-4weeks.txt b/templates/email/emptyhomes/cy/questionnaire-4weeks.txt
new file mode 100644
index 000000000..c4c9d80aa
--- /dev/null
+++ b/templates/email/emptyhomes/cy/questionnaire-4weeks.txt
@@ -0,0 +1,25 @@
+Subject: Holiadur ar eich adroddiad am eiddo gwag
+
+Helo <?=$values['name']?>,
+
+Bedair wythnos yn ôl, fe wnaethoch adrodd am eiddo gwag ar ReportEmptyHomes.com gan roi'r
+manylion sydd wedi'u cynnwys ar ddiwedd y neges e-bost hon. Er mwyn cadw'n safle'n gyfredol
+ac yn berthnasol, byddwn yn ddiolchgar petaech yn gallu llenwi'r holiadur byr hwn
+i ddweud wrthom beth sydd wedi digwydd:
+
+ <?=$values['url']?>
+
+Peidiwch ag ateb y neges e-bost hon; mae blwch sylwadau cyhoeddus
+ar yr holiadur.
+
+Yn gywir,
+reportemptyhomes.com
+
+Roedd eich adroddiad fel a ganlyn:
+
+<?=$values['title']?>
+
+Math o eiddo: <?=$values['category']?>
+
+<?=$values['detail']?>
+
diff --git a/templates/email/emptyhomes/cy/submit.txt b/templates/email/emptyhomes/cy/submit.txt
new file mode 100644
index 000000000..57c0d58b7
--- /dev/null
+++ b/templates/email/emptyhomes/cy/submit.txt
@@ -0,0 +1,47 @@
+Subject: Adroddiad am eiddo gwag
+
+Annwyl Swyddog Eiddo Gwag,
+
+Dyma gyfeiriad newydd am eiddo gwag yn eich ardal, a wnaed gan ddefnyddiwr
+gwefan ReportEmptyHomes.com; dywedwyd wrth ddefnyddiwr y wefan fod yr achos
+wedi cael ei gyfeirio atoch. Byddem yn ddiolchgar petaech yn gwneud yr hyn y
+gallwch i helpu adfer yr eiddo hwn i'w ddefnyddio eto. Byddwn yn cysylltu â'r
+defnyddiwr ymhen mis ac eto ymhen chwe mis ac yn gofyn iddynt beth sydd wedi
+digwydd i'r eiddo.
+
+Hoffwn eich annog chi i ddweud wrthom beth rydych chi wedi'i wneud, a phan y
+daw'r eiddo yn ôl mewn defnydd, trwy lenwi diweddariad wrth y cyfeiriad at yr
+eiddo ar y wefan:
+
+ <?=$values['url']?>
+
+Mae hyn yn rhoi adborth defnyddiol i ddefnyddwyr y wefan ac yn helpu iddynt
+ddeall pa gamau yr ydych yn eu cymryd. Byddwn yn cynnig cyngor i'r defnyddiwr
+ar gamau eraill y gall eu cymryd os nad eir i'r afael â'r eiddo'n llwyddiannus.
+
+<?=$values['has_photo']?>Os hoffech gael help neu gyngor ar gael eiddo gwag yn
+ôl mewn defnydd mae llawer o wybodaeth ddefnyddiol ar wefan yr Asiantaeth Tai
+Gwag www.EmptyHomes.com - os oes gennych unrhyw gwestiynau eraill, rhowch alwad
+i ni.
+
+----------
+
+Enw: <?=$values['name']?>
+
+E-bost: <?=$values['email']?>
+
+<?=$values['phone_line']?>Pwnc: <?=$values['title']?>
+
+Math o eiddo: <?=$values['category']?>
+
+Manylion: <?=$values['detail']?>
+
+<?=$values['closest_address']?>----------
+
+Bydd ymatebion i'r neges e-bost hon yn mynd at y defnyddiwr a gyflwynodd yr
+adroddiad, os hoffech ofyn am fwy o wybodaeth.
+
+Yn gywir,
+reportemptyhomes.com
+
+
diff --git a/templates/email/emptyhomes/cy/update-confirm.txt b/templates/email/emptyhomes/cy/update-confirm.txt
new file mode 100644
index 000000000..655844adf
--- /dev/null
+++ b/templates/email/emptyhomes/cy/update-confirm.txt
@@ -0,0 +1,18 @@
+Subject: Cadarnhewch eich diweddariad ar reportemptyhomes.com
+
+Helo [% update.user.name %],
+
+Cliciwch ar y ddolen isod i gadarnhau'r diweddariad yr ydych newydd ei
+ysgrifennu:
+
+[% token_url %]
+
+Os nad ydych yn gallu clicio ar y ddolen, dylech ei chopïo a'i gludo
+ym mar cyfeiriad eich porwr gwe.
+
+Mae eich diweddariad yn darllen fel a ganlyn:
+
+[% update.text %]
+
+Yn gywir,
+Tîm yr Asiantaeth Tai Gwag
diff --git a/templates/email/emptyhomes/en-gb/alert-confirm.txt b/templates/email/emptyhomes/en-gb/alert-confirm.txt
new file mode 100644
index 000000000..31952be28
--- /dev/null
+++ b/templates/email/emptyhomes/en-gb/alert-confirm.txt
@@ -0,0 +1,14 @@
+Subject: Confirm your alert on reportemptyhomes.com
+
+Hi,
+
+Please click on the link below to confirm the alert you just
+asked to subscribe to on reportemptyhomes.com:
+
+ [% token_url %]
+
+If you can't click the link, please copy and paste it to the
+address bar of your web browser.
+
+Yours,
+The reportemptyhomes.com team
diff --git a/templates/emails/emptyhomes/alert-problem-area b/templates/email/emptyhomes/en-gb/alert-problem-area.txt
index 0fb11665c..0fb11665c 100644
--- a/templates/emails/emptyhomes/alert-problem-area
+++ b/templates/email/emptyhomes/en-gb/alert-problem-area.txt
diff --git a/templates/emails/emptyhomes/alert-problem-council b/templates/email/emptyhomes/en-gb/alert-problem-council.txt
index 6c2d5b55c..6c2d5b55c 100644
--- a/templates/emails/emptyhomes/alert-problem-council
+++ b/templates/email/emptyhomes/en-gb/alert-problem-council.txt
diff --git a/templates/emails/emptyhomes/alert-problem-nearby b/templates/email/emptyhomes/en-gb/alert-problem-nearby.txt
index abe027d20..abe027d20 100644
--- a/templates/emails/emptyhomes/alert-problem-nearby
+++ b/templates/email/emptyhomes/en-gb/alert-problem-nearby.txt
diff --git a/templates/emails/emptyhomes/alert-problem-ward b/templates/email/emptyhomes/en-gb/alert-problem-ward.txt
index 14dabc386..14dabc386 100644
--- a/templates/emails/emptyhomes/alert-problem-ward
+++ b/templates/email/emptyhomes/en-gb/alert-problem-ward.txt
diff --git a/templates/emails/emptyhomes/alert-problem b/templates/email/emptyhomes/en-gb/alert-problem.txt
index 30dc10c42..30dc10c42 100644
--- a/templates/emails/emptyhomes/alert-problem
+++ b/templates/email/emptyhomes/en-gb/alert-problem.txt
diff --git a/templates/emails/emptyhomes/alert-update b/templates/email/emptyhomes/en-gb/alert-update.txt
index 9ae295b3e..9ae295b3e 100644
--- a/templates/emails/emptyhomes/alert-update
+++ b/templates/email/emptyhomes/en-gb/alert-update.txt
diff --git a/templates/email/emptyhomes/en-gb/contact.txt b/templates/email/emptyhomes/en-gb/contact.txt
new file mode 100644
index 000000000..446265517
--- /dev/null
+++ b/templates/email/emptyhomes/en-gb/contact.txt
@@ -0,0 +1,5 @@
+Subject: reportemptyhomes.com message: [% subject %]
+
+[% message %]
+
+[ Sent by contact.cgi on [% host %]. IP address [% ip %] ]
diff --git a/templates/email/emptyhomes/en-gb/partial.txt b/templates/email/emptyhomes/en-gb/partial.txt
new file mode 100644
index 000000000..0ec2d4b68
--- /dev/null
+++ b/templates/email/emptyhomes/en-gb/partial.txt
@@ -0,0 +1,14 @@
+Subject: Confirm your report on reportemptyhomes.com
+
+Hi [% report.name || report.email %],
+
+To confirm the report you have uploaded to reportemptyhomes.com via
+[% report.service %], and to check or add any details,
+please visit the following URL:
+
+[% token_url %]
+
+Thanks!
+
+Yours,
+The reportemptyhomes.com team
diff --git a/templates/emails/empty property-confirm b/templates/email/emptyhomes/en-gb/problem-confirm.txt
index b7381eec8..f8ff3e7ca 100644
--- a/templates/emails/empty property-confirm
+++ b/templates/email/emptyhomes/en-gb/problem-confirm.txt
@@ -1,20 +1,20 @@
Subject: Confirm your empty property report
-Hi <?=$values['name']?>,
+Hi [% report.user.name %],
Please click on the link below to confirm the empty property
report you just added to the site:
-<?=$values['url']?>
+[% token_url %]
If your email program does not let you click on this link,
copy and paste it into your web browser and press return.
Your report had the subject:
-<?=$values['title']?>
+[% report.title %]
And details:
-<?=$values['detail']?>
+[% report.detail %]
Yours,
reportemptyhomes.com
diff --git a/templates/emails/questionnaire-eha-26weeks b/templates/email/emptyhomes/en-gb/questionnaire-26weeks.txt
index 758157ec0..758157ec0 100644
--- a/templates/emails/questionnaire-eha-26weeks
+++ b/templates/email/emptyhomes/en-gb/questionnaire-26weeks.txt
diff --git a/templates/emails/questionnaire-eha-4weeks b/templates/email/emptyhomes/en-gb/questionnaire-4weeks.txt
index 436da73d6..436da73d6 100644
--- a/templates/emails/questionnaire-eha-4weeks
+++ b/templates/email/emptyhomes/en-gb/questionnaire-4weeks.txt
diff --git a/templates/emails/submit-eha b/templates/email/emptyhomes/en-gb/submit.txt
index 4bbaed408..4bbaed408 100644
--- a/templates/emails/submit-eha
+++ b/templates/email/emptyhomes/en-gb/submit.txt
diff --git a/templates/email/emptyhomes/en-gb/update-confirm.txt b/templates/email/emptyhomes/en-gb/update-confirm.txt
new file mode 100644
index 000000000..8facbcf07
--- /dev/null
+++ b/templates/email/emptyhomes/en-gb/update-confirm.txt
@@ -0,0 +1,17 @@
+Subject: Confirm your update on reportemptyhomes.com
+
+Hi [% update.user.name %],
+
+Please click on the link below to confirm the update you just wrote:
+
+[% token_url %]
+
+If your email program does not let you click on this link,
+copy and paste it into your web browser and press return.
+
+Your update reads:
+
+[% update.text %]
+
+Yours,
+The reportemptyhomes.com team
diff --git a/templates/email/fiksgatami/alert-confirm.txt b/templates/email/fiksgatami/alert-confirm.txt
new file mode 100644
index 000000000..7498df2e1
--- /dev/null
+++ b/templates/email/fiksgatami/alert-confirm.txt
@@ -0,0 +1,14 @@
+Subject: Bekreft din sak på FiksGataMI.no
+
+Hei,
+
+Vennligst klikk på lenken under for å bekrefte varslet du
+har bedt om å abonnere på FiksGataMi:
+
+ [% token_url %]
+
+Hvis du ikke kan klikke på lenken, kan du kopiere den i
+addressefeltet på dine nettleser.
+
+Vennlig Hilsen,
+FiksGataMi-gruppen
diff --git a/templates/email/fiksgatami/alert-problem-area.txt b/templates/email/fiksgatami/alert-problem-area.txt
new file mode 100644
index 000000000..93819d6f2
--- /dev/null
+++ b/templates/email/fiksgatami/alert-problem-area.txt
@@ -0,0 +1,13 @@
+Subject: Nye saker i <?=$values['area_name']?> sendt via FiksGataMi.no
+
+Følgende saker er sendt til
+<?=$values['area_name']?>:
+
+<?=$values['data']?>
+
+Vennlig hilsen,
+FiksGataMi-gruppen
+
+Hvis du ikke ønsker å motta epost om saker som er sendt til
+<?=$values['area_name']?> i fremtiden, klikk på følgende link:
+<?=$values['unsubscribe_url']?>
diff --git a/templates/email/fiksgatami/alert-problem-council.txt b/templates/email/fiksgatami/alert-problem-council.txt
new file mode 100644
index 000000000..54a51c7b2
--- /dev/null
+++ b/templates/email/fiksgatami/alert-problem-council.txt
@@ -0,0 +1,12 @@
+Subject: Nye saker sendt til <?=$values['area_name']?> via FiksGataMi.no
+
+Følgende saker er sendt til <?=$values['area_name']?>:
+
+<?=$values['data']?>
+
+Vennlig hilsen,
+FiksGataMi-gruppen
+
+Hvis du ikke ønsker å motta epost om saker som er sendt til
+<?=$values['area_name']?> i fremtiden, klikk på følgende link:
+<?=$values['unsubscribe_url']?>
diff --git a/templates/email/fiksgatami/alert-problem-nearby.txt b/templates/email/fiksgatami/alert-problem-nearby.txt
new file mode 100644
index 000000000..b470de203
--- /dev/null
+++ b/templates/email/fiksgatami/alert-problem-nearby.txt
@@ -0,0 +1,11 @@
+Subject: Nye saker i nærheten på FiksGataMi.no
+
+Følgende saker i nærheten er sendt via FiksGataMi.no:
+
+<?=$values['data']?>
+
+Vennlig hilsen,
+FiksGataMi-gruppen
+
+Hvis du ikke ønsker å abonnere på epost om problemer i nærheten,
+klikk på følgende link: <?=$values['unsubscribe_url']?>
diff --git a/templates/email/fiksgatami/alert-problem-ward.txt b/templates/email/fiksgatami/alert-problem-ward.txt
new file mode 100644
index 000000000..2fbf0b068
--- /dev/null
+++ b/templates/email/fiksgatami/alert-problem-ward.txt
@@ -0,0 +1,13 @@
+Subject: Nye saker sendt til <?=$values['area_name']?> innenfor <?=$values['ward_name']?> via FiksGataMi.no
+
+Følgende saker er sendt til <?=$values['area_name']?>
+innenfor <?=$values['ward_name']?>:
+
+<?=$values['data']?>
+
+Vennlig hilsen,
+FiksGataMi-gruppen
+
+Hvis du ikke ønsker å abonnere via epost på saker som er sendt til
+<?=$values['area_name']?> innenfor <?=$values['ward_name']?>,
+klikk på denne linken: <?=$values['unsubscribe_url']?>
diff --git a/templates/email/fiksgatami/alert-problem.txt b/templates/email/fiksgatami/alert-problem.txt
new file mode 100644
index 000000000..a62745bef
--- /dev/null
+++ b/templates/email/fiksgatami/alert-problem.txt
@@ -0,0 +1,11 @@
+Subject: Ny saker på FiksGataMi.no
+
+Følgende ny saker er lagt til:
+
+<?=$values['data']?>
+
+Vennlig hilsen,
+FiksGataMi-gruppen
+
+Hvis du ikke ønsker å stå på denne epostlisten,
+klikk på linken: <?=$values['unsubscribe_url']?>
diff --git a/templates/email/fiksgatami/alert-update.txt b/templates/email/fiksgatami/alert-update.txt
new file mode 100644
index 000000000..80bc8dbf8
--- /dev/null
+++ b/templates/email/fiksgatami/alert-update.txt
@@ -0,0 +1,18 @@
+Subject: Nye oppdateringer for problem - '<?=$values['title']?>'
+
+Følgende oppdateringer har blitt lagt inn for dette problemet:
+
+<?=$values['data']?>
+
+<?=$values['state_message']?>
+
+For å se på eller svare på disse oppdateringene, besøk denne URLen:
+ <?=$values['problem_url']?>
+
+Du kan ikke kontakte noen ved å svare på denne eposten.
+
+Vennlig hilsen,
+Fiksgata-laget
+
+For å slutte å motta epost når det er nye oppdateringer om dettte
+problemet, følg denne lenken: <?=$values['unsubscribe_url']?>
diff --git a/templates/email/fiksgatami/contact.txt b/templates/email/fiksgatami/contact.txt
new file mode 100644
index 000000000..59a778ad5
--- /dev/null
+++ b/templates/email/fiksgatami/contact.txt
@@ -0,0 +1,5 @@
+Subject: FMS message: [% subject %]
+
+[% message %]
+
+[ Sent by contact.cgi on [% host %]. IP address [% ip %] ]
diff --git a/templates/email/fiksgatami/partial.txt b/templates/email/fiksgatami/partial.txt
new file mode 100644
index 000000000..70359e265
--- /dev/null
+++ b/templates/email/fiksgatami/partial.txt
@@ -0,0 +1,14 @@
+Subject: Bekreft din rapport på FiksGataMi
+
+Hei [% report.name || report.email %],
+
+For å bekrefte rapporten du har lastet opp til FiksGatami via
+[% report.service %], og for å sjekke eller legge til litt detaljer,
+besøk følgende URL:
+
+[% token_url %]
+
+Takk!
+
+Vennlig hilsen,
+FiksGataMi-gruppen
diff --git a/templates/email/fiksgatami/problem-confirm.txt b/templates/email/fiksgatami/problem-confirm.txt
new file mode 100644
index 000000000..046903270
--- /dev/null
+++ b/templates/email/fiksgatami/problem-confirm.txt
@@ -0,0 +1,20 @@
+Subject: Bekreftelse på ny sak lagt til på FiksGataMi.no
+
+Hei [% report.user.name %],
+
+Vennligst klikk på linken under for å bekrefte saken du har lagt til
+på FiksGataMi:
+
+[% token_url %]
+
+Hvis ditt epost program ikke gir deg mulighet til å klikke på linken,
+kopier linken inn i nettleseren.
+
+Din sak hadde følgende tittel:
+[% report.title %]
+
+Med følgende verdier:
+[% report.detail %]
+
+Vennlig hilsen,
+FiksGataMi-gruppen
diff --git a/templates/email/fiksgatami/questionnaire.txt b/templates/email/fiksgatami/questionnaire.txt
new file mode 100644
index 000000000..fd16c42b1
--- /dev/null
+++ b/templates/email/fiksgatami/questionnaire.txt
@@ -0,0 +1,23 @@
+Subject: Skjema for din sak på FiksGataMi
+
+Hei <?=$values['name']?>,
+
+for <?=$values['created']?> siden, la du til en sak på FiksGataMi
+med detaljene som vist i denne eposten. For å holde vår nettside oppdatert
+og relevant, vil vi sette pris på om du kunne fylle ut følgende skjema for å
+oppdatere saken:
+
+ <?=$values['url']?>
+
+Vennligst ikke svar på denne eposten; det er muligheter for å kommentere i skjema.
+
+Vennlig hilsen,
+FiksGataMi-gruppen
+
+Saken du la til var som følger:
+
+<?=$values['title']?>
+
+<?=$values['detail']?>
+
+
diff --git a/templates/email/fiksgatami/submit.txt b/templates/email/fiksgatami/submit.txt
new file mode 100644
index 000000000..12c801bbb
--- /dev/null
+++ b/templates/email/fiksgatami/submit.txt
@@ -0,0 +1,41 @@
+Subject: Problemrapport: <?=$values['title']?>
+
+Til <?=$values['councils_name']?>,
+
+<?=$values['missing']?><?=$values['multiple']?>En bruker av
+FiksGataMi har sendt inn følgende rapport om et lokalt
+problem som vi tror trenger deres oppmerksomhet.
+
+<?=$values['fuzzy']?>, eller for å bidra med en oppdatering om problemet,
+vennligst besøk følgende lenke:
+
+ <?=$values['url']?>
+
+<?=$values['has_photo']?>----------
+
+Navn: <?=$values['name']?>
+
+Epost: <?=$values['email']?>
+
+<?=$values['phone_line']?><?=$values['category_line']?>Tema: <?=$values['title']?>
+
+Detajer: <?=$values['detail']?>
+
+<?=$values['easting_northing']?>Breddegrad: <?=$values['latitude']?>
+
+Lengegrad: <?=$values['longitude']?>
+
+<?=$values['closest_address']?>----------
+
+Svar på denne eposten går til brukeren som sendte inn problemet.
+
+Vennlig hilsen,
+FiksGataMi-gruppen
+
+[ Denne meldingen ble sendt inn via FiksGataMi, et prosjekt hos foreningen NUUG.
+Hvis det er en mer passende epostadresse for meldinger om
+<?=$values['category_footer']?>, vær så snill å gi oss beskjed ved å besøke
+<http://www.fiksgatami.no/contact>.
+Dette vil bidra til å forbedre tjenesten for lokalbefolkningen. Vi
+tar også gjerne imot andre tilbakemeldinger om du har noen. ]
+
diff --git a/templates/email/fiksgatami/update-confirm.txt b/templates/email/fiksgatami/update-confirm.txt
new file mode 100644
index 000000000..70e1c2444
--- /dev/null
+++ b/templates/email/fiksgatami/update-confirm.txt
@@ -0,0 +1,18 @@
+Subject: Bekreft din oppdatering på FiksGataMi
+
+Hei [% update.user.name %],
+
+Vennligst klikk på lenken under for å bekrefte oppdateringen du skrev
+nettopp:
+
+[% token_url %]
+
+Hvis du ikke kan klikke på lenken, kopier og lim den inn
+i adressefeltet på din nettleser..
+
+Din oppdatering inneholder:
+
+[% update.text %]
+
+Vennlig hilsen,
+FiksGataMi-gruppen
diff --git a/templates/emails/.gitignore b/templates/emails/.gitignore
deleted file mode 100644
index 911c95650..000000000
--- a/templates/emails/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/cities \ No newline at end of file
diff --git a/templates/emails/flickr-confirm b/templates/emails/flickr-confirm
deleted file mode 100644
index 45d11a28a..000000000
--- a/templates/emails/flickr-confirm
+++ /dev/null
@@ -1,14 +0,0 @@
-Subject: Confirm your email address on FixMyStreet
-
-Hi,
-
-Please click on the link below to confirm the email address
-you just gave to FixMyStreet:
-
-<?=$values['url']?>
-
-This is so we can look up the photos you tag with FixMyStreet,
-and send you an email letting you know about your new problems.
-
-Yours,
-The FixMyStreet team
diff --git a/templates/emails/flickr-submit b/templates/emails/flickr-submit
deleted file mode 100644
index f7eb1e99b..000000000
--- a/templates/emails/flickr-submit
+++ /dev/null
@@ -1,14 +0,0 @@
-Subject: New photo pulled from Flickr to FixMyStreet
-
-Hi <?=$values['name']?>,
-
-We've fetched a photo you uploaded to Flickr and tagged with
-FixMyStreet. To check the details we have, and to add any more,
-please visit the following URL:
-
-<?=$values['url']?>
-
-Then we can send your photo to the council. Thanks!
-
-Yours,
-The FixMyStreet team
diff --git a/templates/emails/tms-confirm b/templates/emails/tms-confirm
deleted file mode 100644
index 4821aed82..000000000
--- a/templates/emails/tms-confirm
+++ /dev/null
@@ -1,14 +0,0 @@
-Subject: Confirm your expression of interest in TextMyStreet
-
-Hi,
-
-Please click on the link below to confirm your expression of
-interest in TextMyStreet:
-
- <?=$values['url']?>
-
-If you can't click the link, please copy and paste it to the
-address bar of your web browser.
-
-Yours,
-The FixMyStreet team
diff --git a/templates/emails/update-confirm b/templates/emails/update-confirm
deleted file mode 100644
index 11878ad0b..000000000
--- a/templates/emails/update-confirm
+++ /dev/null
@@ -1,17 +0,0 @@
-Subject: Confirm your update on FixMyStreet
-
-Hi <?=$values['name']?>,
-
-Please click on the link below to confirm the update you just wrote:
-
-<?=$values['url']?>
-
-If you can't click the link, please copy and paste it to the
-address bar of your web browser.
-
-Your update reads:
-
-<?=$values['update']?>
-
-Yours,
-The FixMyStreet team
diff --git a/templates/website/cobrands/barnet/footer b/templates/web/barnet/footer.html
index 40b9adce0..9bbeaa7e9 100644
--- a/templates/website/cobrands/barnet/footer
+++ b/templates/web/barnet/footer.html
@@ -1,151 +1,151 @@
- </div>
- <br class="cl">
- </div><!-- end content -->
- </div><!-- end wrap -->
- <div id="left-column">
-
-
- <div id="navigation">
- <h2>Main Menu</h2>
- <ul>
- <li class="section"><a href="/">FixMyStreet</a>
- <ul>
- <li class="section">{{ ($ENV{REQUEST_URI} eq '/') ? '<strong>Report a problem</strong>' : '<a href="/">Report a problem</a>' }}</li>
- <li class="section">{{ ($ENV{REQUEST_URI} eq '/reports/Barnet') ? '<strong>All reports</strong>' : '<a href="/reports/Barnet">All reports</a>' }}</li>
- <li class="section">{{ ($ENV{REQUEST_URI} eq '/alert') ? '<strong>Local alerts</strong>' : '<a href="/alert">Local alerts</a>' }}</li>
- <li class="section">{{ ($ENV{REQUEST_URI} eq '/faq') ? '<strong>Help</strong>' : '<a href="/faq">Help</a>' }}</li>
- <li class="section">{{ ($ENV{REQUEST_URI} eq '/contact') ? '<strong>Contact</strong>' : '<a href="/contact">Contact</a>' }}</li>
- </ul>
- </ul>
- </div> <!-- end navigation -->
-
- <div id="online-services">
- <h2>Online Services</h2>
- <ul>
- <li><a href="http://www.barnet.gov.uk/">Barnet Council</a></li>
- </ul>
- <!-- <p class="browse-aloud">Listen to this site using <br><a href="#">Browser Aloud</a></p> -->
- </div> <!-- end online-services -->
-
-<!--
- <div id="useful-links">
- <h2>Useful Links</h2>
- <ul>
- <li><a href="http://www.barnet.gov.uk/">Council homepage</a></li>
- </ul>
- </div>
--->
-
- <div id="contact">
- <h2>Contact</h2>
- <dl>
- <dt>Council Address</dt>
- <dd>
- North London Business Park (NLBP),<br>
- Oakleigh Road South,<br>
- London.<br>
- N11 1NP<br>
- <a href="http://maps.google.co.uk/maps?f=q&hl=en&geocode=&q=N11+1NP+&sll=53.800651,-4.064941&sspn=11.823255,39.550781&ie=UTF8&ll=51.624877,-0.152156&spn=0.024244,0.077248&t=h&z=14">View map of Barnet</a>
- </dd>
- <dt>Phone Number</dt>
- <dd>020 8359 2000</dd>
- <dt>Text Number (SMS)</dt>
- <dd>07781 473279</dd>
- <dt>Fax Number</dt>
- <dd>020 8359 4156</dd>
- <dt>Typetalk</dt>
- <dd>18001 020 8359 2040</dd>
- <dt>Email</dt>
- <dd><a href="mailto:first.contact@barnet.gov.uk">first.contact@barnet.gov.uk</a></dd>
- </dl>
- </div> <!-- end useful-links -->
-
- </div><!-- end left-column -->
-
-
-<br class="cl">
-
- <div id="pre-footer">
-
- <div class="box-left">
-
- <dl>
- <dt>Council Address</dt>
- <dd>
- North London Business Park (NLBP),<br>
- Oakleigh Road South,<br>
- London.<br>
- N11 1NP<br>
- <a href="http://maps.google.co.uk/maps?f=q&hl=en&geocode=&q=N11+1NP+&sll=53.800651,-4.064941&sspn=11.823255,39.550781&ie=UTF8&ll=51.624877,-0.152156&spn=0.024244,0.077248&t=h&z=14">View map of Barnet</a>
- </dd>
- </dl>
- <dl>
- <dt>Phone Number</dt>
- <dd>020 8359 2000</dd>
- <dt>Text Number (SMS)</dt>
- <dd>07781 473279</dd>
- <dt>Fax Number</dt>
- <dd>020 8359 4156</dd>
- </dl>
- <dl>
- <dt>Typetalk</dt>
- <dd>18001 020 8359 2040</dd>
- <dt>Email</dt>
- <dd><a href="mailto:first.contact@barnet.gov.uk">first.contact@barnet.gov.uk</a></dd>
- </dl>
- </div>
- <div class="box-right">
- <!--
- <div class="social-bookmarking">
- <h2>Add this page to your social bookmarks:</h2>
- <ul>
- <li class="facebook"><a href="#">Facebook</a></li>
- <li class="delicious"><a href="#">delicious</a></li>
- <li class="technorati"><a href="#">Technorati</a></li>
- <li class="stumbleupon"><a href="#">StumbleUpon</a></li>
- <li class="google"><a href="#">Google</a></li>
- <li class="digg"><a href="#">Digg</a></li>
- </ul>
- <p>(<a href="#">What's social bookmarking?</a>)</p>
- </div>
- -->
- </div>
-
-<br class="cl">
- </div><!-- end pre-footer -->
-
-
-
-
- <div id="footer">
- <ul>
- <li><a href="http://www.barnet.gov.uk/copyright">Copyright</a></li>
- <li><a href="http://www.barnet.gov.uk/disclaimer">Disclaimer</a></li>
- <li><a href="http://www.barnet.gov.uk/privacy">Privacy Statement</a></li>
- <li><a href="http://www.barnet.gov.uk/website-accessibility">Accessibility Statement</a></li>
- <li><a href="http://www.barnet.gov.uk/atoz">A-Z</a></li>
- <li><a href="http://www.barnet.gov.uk/faq">FAQs</a></li>
- <li><a href="http://www.barnet.gov.uk/contact-us">Contact&nbsp;Us</a></li>
- <li><a href="http://www.barnet.gov.uk/help">Help</a></li>
- <li><a href="http://www.barnet.gov.uk/sitemap">Sitemap</a></li>
- <li><a href="http://www.barnet.gov.uk/what%27s_on">What's On</a></li>
- </ul>
- </div><!-- end footer -->
-
- </div><!-- end iewrap -->
-
-<!-- Piwik -->
-<script type="text/javascript">
-var pkBaseURL = (("https:" == document.location.protocol) ? "https://piwik.mysociety.org/" : "http://piwik.mysociety.org/");
-document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
-</script><script type="text/javascript">
-try {
-var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", 12);
-piwikTracker.trackPageView();
-piwikTracker.enableLinkTracking();
-} catch( err ) {}
-</script><noscript><img width=1 height=1 src="http://piwik.mysociety.org/piwik.php?idsite=12" style="border:0" alt=""></noscript>
-<!-- End Piwik Tag -->
-
- </body>
-</html>
+ </div>
+ <br class="cl">
+ </div><!-- end content -->
+ </div><!-- end wrap -->
+ <div id="left-column">
+
+
+ <div id="navigation">
+ <h2>Main Menu</h2>
+ <ul>
+ <li class="section"><a href="/">FixMyStreet</a>
+ <ul>
+ <li class="section">[% c.req.uri.path == '/' ? '<strong>Report a problem</strong>' : '<a href="/">Report a problem</a>' %]</li>
+ <li class="section">[% c.req.uri.path == '/reports/Barnet' ? '<strong>All reports</strong>' : '<a href="/reports/Barnet">All reports</a>' %]</li>
+ <li class="section">[% c.req.uri.path == '/alert' ? '<strong>Local alerts</strong>' : '<a href="/alert">Local alerts</a>' %]</li>
+ <li class="section">[% c.req.uri.path == '/faq' ? '<strong>Help</strong>' : '<a href="/faq">Help</a>' %]</li>
+ <li class="section">[% c.req.uri.path == '/contact' ? '<strong>Contact</strong>' : '<a href="/contact">Contact</a>' %]</li>
+ </ul>
+ </ul>
+ </div> <!-- end navigation -->
+
+ <div id="online-services">
+ <h2>Online Services</h2>
+ <ul>
+ <li><a href="http://www.barnet.gov.uk/">Barnet Council</a></li>
+ </ul>
+ <!-- <p class="browse-aloud">Listen to this site using <br><a href="#">Browser Aloud</a></p> -->
+ </div> <!-- end online-services -->
+
+<!--
+ <div id="useful-links">
+ <h2>Useful Links</h2>
+ <ul>
+ <li><a href="http://www.barnet.gov.uk/">Council homepage</a></li>
+ </ul>
+ </div>
+-->
+
+ <div id="contact">
+ <h2>Contact</h2>
+ <dl>
+ <dt>Council Address</dt>
+ <dd>
+ North London Business Park (NLBP),<br>
+ Oakleigh Road South,<br>
+ London.<br>
+ N11 1NP<br>
+ <a href="http://maps.google.co.uk/maps?f=q&hl=en&geocode=&q=N11+1NP+&sll=53.800651,-4.064941&sspn=11.823255,39.550781&ie=UTF8&ll=51.624877,-0.152156&spn=0.024244,0.077248&t=h&z=14">View map of Barnet</a>
+ </dd>
+ <dt>Phone Number</dt>
+ <dd>020 8359 2000</dd>
+ <dt>Text Number (SMS)</dt>
+ <dd>07781 473279</dd>
+ <dt>Fax Number</dt>
+ <dd>020 8359 4156</dd>
+ <dt>Typetalk</dt>
+ <dd>18001 020 8359 2040</dd>
+ <dt>Email</dt>
+ <dd><a href="mailto:first.contact@barnet.gov.uk">first.contact@barnet.gov.uk</a></dd>
+ </dl>
+ </div> <!-- end useful-links -->
+
+ </div><!-- end left-column -->
+
+
+<br class="cl">
+
+ <div id="pre-footer">
+
+ <div class="box-left">
+
+ <dl>
+ <dt>Council Address</dt>
+ <dd>
+ North London Business Park (NLBP),<br>
+ Oakleigh Road South,<br>
+ London.<br>
+ N11 1NP<br>
+ <a href="http://maps.google.co.uk/maps?f=q&hl=en&geocode=&q=N11+1NP+&sll=53.800651,-4.064941&sspn=11.823255,39.550781&ie=UTF8&ll=51.624877,-0.152156&spn=0.024244,0.077248&t=h&z=14">View map of Barnet</a>
+ </dd>
+ </dl>
+ <dl>
+ <dt>Phone Number</dt>
+ <dd>020 8359 2000</dd>
+ <dt>Text Number (SMS)</dt>
+ <dd>07781 473279</dd>
+ <dt>Fax Number</dt>
+ <dd>020 8359 4156</dd>
+ </dl>
+ <dl>
+ <dt>Typetalk</dt>
+ <dd>18001 020 8359 2040</dd>
+ <dt>Email</dt>
+ <dd><a href="mailto:first.contact@barnet.gov.uk">first.contact@barnet.gov.uk</a></dd>
+ </dl>
+ </div>
+ <div class="box-right">
+ <!--
+ <div class="social-bookmarking">
+ <h2>Add this page to your social bookmarks:</h2>
+ <ul>
+ <li class="facebook"><a href="#">Facebook</a></li>
+ <li class="delicious"><a href="#">delicious</a></li>
+ <li class="technorati"><a href="#">Technorati</a></li>
+ <li class="stumbleupon"><a href="#">StumbleUpon</a></li>
+ <li class="google"><a href="#">Google</a></li>
+ <li class="digg"><a href="#">Digg</a></li>
+ </ul>
+ <p>(<a href="#">What's social bookmarking?</a>)</p>
+ </div>
+ -->
+ </div>
+
+<br class="cl">
+ </div><!-- end pre-footer -->
+
+
+
+
+ <div id="footer">
+ <ul>
+ <li><a href="http://www.barnet.gov.uk/copyright">Copyright</a></li>
+ <li><a href="http://www.barnet.gov.uk/disclaimer">Disclaimer</a></li>
+ <li><a href="http://www.barnet.gov.uk/privacy">Privacy&nbsp;Statement</a></li>
+ <li><a href="http://www.barnet.gov.uk/website-accessibility">Accessibility&nbsp;Statement</a></li>
+ <li><a href="http://www.barnet.gov.uk/atoz">A-Z</a></li>
+ <li><a href="http://www.barnet.gov.uk/faq">FAQs</a></li>
+ <li><a href="http://www.barnet.gov.uk/contact-us">Contact&nbsp;Us</a></li>
+ <li><a href="http://www.barnet.gov.uk/help">Help</a></li>
+ <li><a href="http://www.barnet.gov.uk/sitemap">Sitemap</a></li>
+ <li><a href="http://www.barnet.gov.uk/what%27s_on">What's&nbsp;On</a></li>
+ </ul>
+ </div><!-- end footer -->
+
+ </div><!-- end iewrap -->
+
+<!-- Piwik -->
+<script type="text/javascript">
+var pkBaseURL = (("https:" == document.location.protocol) ? "https://piwik.mysociety.org/" : "http://piwik.mysociety.org/");
+document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
+</script><script type="text/javascript">
+try {
+var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", 12);
+piwikTracker.trackPageView();
+piwikTracker.enableLinkTracking();
+} catch( err ) {}
+</script><noscript><img width=1 height=1 src="http://piwik.mysociety.org/piwik.php?idsite=12" style="border:0" alt=""></noscript>
+<!-- End Piwik Tag -->
+
+ </body>
+</html>
diff --git a/templates/website/cobrands/barnet/header b/templates/web/barnet/header.html
index cd274df7b..8346ea344 100644
--- a/templates/website/cobrands/barnet/header
+++ b/templates/web/barnet/header.html
@@ -1,98 +1,93 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html lang="{{ $lang_code }}">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <link rel="stylesheet" type="text/css" href="/cobrands/barnet/css/basic.css">
-
- <style type="text/css">
- @import url(/css/core.css);
- @import url(/cobrands/barnet/css/layout.css);
- </style>
-
-
- <!-- Preferred style sheet enabled when the page is loaded -->
- <link rel="stylesheet" title="default" type="text/css" href="/cobrands/barnet/css/light.css">
-
- <!-- Alternate style sheets used for the light/relaxed style -->
- <link rel="alternate stylesheet" title="relaxed light" type="text/css" href="/cobrands/barnet/css/light.css">
- <link rel="alternate stylesheet" title="relaxed light" type="text/css" href="/cobrands/barnet/css/relaxed.css">
-
- <!-- Alternate style sheets used for the dark style -->
- <link rel="alternate stylesheet" title="dark" type="text/css" href="/cobrands/barnet/css/dark.css">
-
- <!-- Alternate style sheets used for the dark/relaxed style -->
- <link rel="alternate stylesheet" title="relaxed dark" type="text/css" href="/cobrands/barnet/css/dark.css">
- <link rel="alternate stylesheet" title="relaxed dark" type="text/css" href="/cobrands/barnet/css/relaxed.css">
-
-
- <!--[if lte IE 6]>
- <link rel="stylesheet" type="text/css" href="/cobrands/barnet/css/ie.css">
-
- <link rel="alternate stylesheet" title="relaxed light" type="text/css" href="/cobrands/barnet/css/ie-relaxed.css">
-
- <link rel="alternate stylesheet" title="dark" type="text/css" href="/cobrands/barnet/css/ie-dark.css">
-
- <link rel="alternate stylesheet" title="relaxed dark" type="text/css" href="/cobrands/barnet/css/ie-dark.css">
- <link rel="alternate stylesheet" title="relaxed dark" type="text/css" href="/cobrands/barnet/css/ie-relaxed.css">
- <![endif]-->
-
- <!--[if IE 7]>
- <link rel="stylesheet" type="text/css" href="/cobrands/barnet/css/ie-seven.css">
-
- <link rel="alternate stylesheet" title="relaxed light" type="text/css" href="/cobrands/barnet/css/ie-seven-relaxed.css">
-
- <link rel="alternate stylesheet" title="relaxed dark" type="text/css" href="/cobrands/barnet/css/ie-seven-relaxed.css">
- <![endif]-->
- <link rel="stylesheet" media="print" type="text/css" href="/cobrands/barnet/css/print.css">
-
-<script type="text/javascript" src="/cobrands/barnet/javascript/jquery-1.2.6_common.js"></script>
-<script type="text/javascript" src="/cobrands/barnet/javascript/styleswitch.js"></script>
-<script type="text/javascript" src="/cobrands/barnet/javascript/jquery-cookie-min.js"></script>
-<script type="text/javascript" src="/cobrands/barnet/javascript/2008-portsurf.js"></script>
-<script type="text/javascript" src="/yui/utilities.js"></script>
-<script type="text/javascript" src="/js.js"></script>
-{{ $map_js }}
-
-{{ $rss }}
-
- <title>{{ $title }}{{ $site_title }}</title>
- </head>
- <body>
-
- <div id="iewrap">
- <img id="print-logo" src="/cobrands/barnet/images/logo-barnet.gif" alt="">
- <div id="header">
- <a href="http://www.barnet.gov.uk/"><img src="/cobrands/barnet/images/logo-barnet.gif" alt="Barnet Logo"></a>
-
- <a href="#content" class="rm">Skip to Content</a>
- <span class="rm">|</span>
- <a href="#navigation" class="rm">Skip to Menu</a>
- <span class="rm">|</span>
-
- <form action="http://www.barnet.gov.uk/search">
- <div class="search-input">
- <input type="hidden" name="col" value="lbb">
- <input type="hidden" name="type" value="sitesearch">
- <label for="input-0" class="rm">Search:</label>
- <input type="text" value="Please enter your search" name="search" class="search">
- </div>
- <div class="search-button">
- <input type="submit" value="Search" class="submit" id="input-0">
- </div>
- </form>
-
- <div id="switcher" style="display:none">
- <p>Site preferences:</p>
- <ul>
- <li class="default"><a id="s1" class="styleswitch" rel="default" title="Light colours with the default layout">default</a></li>
- <li class="relaxed-light"><a id="s2" class="styleswitch" rel="relaxed light" title="Light colours with the relaxed layout" href="#">relaxed light</a></li>
- <li class="dark"><a id="s3" class="styleswitch" rel="dark" title="Dark colours with the default layout" href="#">dark</a></li>
- <li class="relaxed-dark"><a id="s4" class="styleswitch" rel="relaxed dark" title="Dark colours with the relaxed layout" href="#">relaxed dark</a></li>
- </ul>
- </div><!-- end switcher -->
- </div><!-- end header -->
-
- <div id="wrap">
- <div id="content">
- <div id="mysociety">
-
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="[% lang_code %]">
+ <head>
+ <link rel="stylesheet" type="text/css" href="/cobrands/barnet/css/basic.css">
+
+ <style type="text/css">
+ @import url(/css/core.css);
+ @import url(/cobrands/barnet/css/layout.css);
+ </style>
+
+
+ <!-- Preferred style sheet enabled when the page is loaded -->
+ <link rel="stylesheet" title="default" type="text/css" href="/cobrands/barnet/css/light.css">
+
+ <!-- Alternate style sheets used for the light/relaxed style -->
+ <link rel="alternate stylesheet" title="relaxed light" type="text/css" href="/cobrands/barnet/css/light.css">
+ <link rel="alternate stylesheet" title="relaxed light" type="text/css" href="/cobrands/barnet/css/relaxed.css">
+
+ <!-- Alternate style sheets used for the dark style -->
+ <link rel="alternate stylesheet" title="dark" type="text/css" href="/cobrands/barnet/css/dark.css">
+
+ <!-- Alternate style sheets used for the dark/relaxed style -->
+ <link rel="alternate stylesheet" title="relaxed dark" type="text/css" href="/cobrands/barnet/css/dark.css">
+ <link rel="alternate stylesheet" title="relaxed dark" type="text/css" href="/cobrands/barnet/css/relaxed.css">
+
+
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" type="text/css" href="/cobrands/barnet/css/ie.css">
+
+ <link rel="alternate stylesheet" title="relaxed light" type="text/css" href="/cobrands/barnet/css/ie-relaxed.css">
+
+ <link rel="alternate stylesheet" title="dark" type="text/css" href="/cobrands/barnet/css/ie-dark.css">
+
+ <link rel="alternate stylesheet" title="relaxed dark" type="text/css" href="/cobrands/barnet/css/ie-dark.css">
+ <link rel="alternate stylesheet" title="relaxed dark" type="text/css" href="/cobrands/barnet/css/ie-relaxed.css">
+ <![endif]-->
+
+ <!--[if IE 7]>
+ <link rel="stylesheet" type="text/css" href="/cobrands/barnet/css/ie-seven.css">
+
+ <link rel="alternate stylesheet" title="relaxed light" type="text/css" href="/cobrands/barnet/css/ie-seven-relaxed.css">
+
+ <link rel="alternate stylesheet" title="relaxed dark" type="text/css" href="/cobrands/barnet/css/ie-seven-relaxed.css">
+ <![endif]-->
+ <link rel="stylesheet" media="print" type="text/css" href="/cobrands/barnet/css/print.css">
+
+ <script type="text/javascript" src="/cobrands/barnet/javascript/jquery-1.2.6_common.js"></script>
+ <script type="text/javascript" src="/cobrands/barnet/javascript/styleswitch.js"></script>
+ <script type="text/javascript" src="/cobrands/barnet/javascript/jquery-cookie-min.js"></script>
+ <script type="text/javascript" src="/cobrands/barnet/javascript/2008-portsurf.js"></script>
+
+ [% INCLUDE 'common_header_tags.html' %]
+
+ </head>
+ <body>
+
+ <div id="iewrap">
+ <img id="print-logo" src="/cobrands/barnet/images/logo-barnet.gif" alt="">
+ <div id="header">
+ <a href="http://www.barnet.gov.uk/"><img src="/cobrands/barnet/images/logo-barnet.gif" alt="Barnet Logo"></a>
+
+ <a href="#content" class="rm">Skip to Content</a>
+ <span class="rm">|</span>
+ <a href="#navigation" class="rm">Skip to Menu</a>
+ <span class="rm">|</span>
+
+ <form action="http://www.barnet.gov.uk/search">
+ <div class="search-input">
+ <input type="hidden" name="col" value="lbb">
+ <input type="hidden" name="type" value="sitesearch">
+ <label for="input-0" class="rm">Search:</label>
+ <input type="text" value="Please enter your search" name="search" class="search">
+ </div>
+ <div class="search-button">
+ <input type="submit" value="Search" class="submit" id="input-0">
+ </div>
+ </form>
+
+ <div id="switcher" style="display:none">
+ <p>Site preferences:</p>
+ <ul>
+ <li class="default"><a id="s1" class="styleswitch" rel="default" title="Light colours with the default layout">default</a></li>
+ <li class="relaxed-light"><a id="s2" class="styleswitch" rel="relaxed light" title="Light colours with the relaxed layout" href="#">relaxed light</a></li>
+ <li class="dark"><a id="s3" class="styleswitch" rel="dark" title="Dark colours with the default layout" href="#">dark</a></li>
+ <li class="relaxed-dark"><a id="s4" class="styleswitch" rel="relaxed dark" title="Dark colours with the relaxed layout" href="#">relaxed dark</a></li>
+ </ul>
+ </div><!-- end switcher -->
+ </div><!-- end header -->
+
+ <div id="wrap">
+ <div id="content">
+ <div id="mysociety">
+
diff --git a/templates/web/default/admin/council_contacts.html b/templates/web/default/admin/council_contacts.html
new file mode 100644
index 000000000..e7cacf4f8
--- /dev/null
+++ b/templates/web/default/admin/council_contacts.html
@@ -0,0 +1,89 @@
+[% INCLUDE 'admin/header.html' title=tprintf(loc('Council contacts for %s'), council_name) -%]
+
+<p>
+<em>[% updated %]</em>
+</p>
+
+<p>
+[% IF example_pc %]
+<a href="[% c.uri_for( '/around', { pc => example_pc } ) %]">[% tprintf( loc('Example postcode %s'), example_pc ) | html %]</a> |
+[% END %]
+<a href="[% c.uri_for( '/reports', { council => area_id } ) %]">[% loc('List all reported problems' ) %]</a>
+<a href="[% c.uri_for( 'council_contacts', area_id, { text => 1 } ) %]">[% loc('Text only version') %]</a>
+</p>
+
+<form method="post" action="[% c.uri_for('council_contacts', area_id ) %]" enctype="application/x-www-form-urlencoded" accept-charset="utf-8">
+
+ <table cellspacing="0" cellpadding="2" border="1">
+ <tr>
+ <th>[% loc('Category') %]</th>
+ <th>[% loc('Email') %]</th>
+ <th>[% loc('Confirmed') %]</th>
+ <th>[% loc('Deleted') %]</th>
+ <th>[% loc('Last editor') %]</th>
+ <th>[% loc('Note') %]</th>
+ <th>[% loc('When edited') %]</th>
+ <th>[% loc('Confirm') %]</th>
+ </tr>
+ [% WHILE ( contact = contacts.next ) %]
+ <tr>
+ <td><a href="[% c.uri_for( 'council_edit', area_id, contact.category ) %]">[% contact.category %]</a></td>
+ <td>[% contact.email | html %]</td>
+ <td>[% IF contact.confirmed %][% loc('Yes') %][% ELSE %][% loc('No') %][% END %]</td>
+ <td>[% IF contact.deleted %][% loc('Yes') %][% ELSE %][% loc('No') %][% END %]</td>
+ <td>[% contact.editor %]</td>
+ <td>[% contact.note | html %]</td>
+ <td>[% contact.whenedited.ymd _ ' ' _ contact.whenedited.hms %]</td>
+ <td><input type="checkbox" name="confirmed" value="[% contact.category %]"></td>
+ </tr>
+ [% END %]
+ </table>
+
+ <p>
+ <input type="hidden" name="area_id" value="[% area_id %]">
+ <input type="hidden" name="posted" value="update">
+ <input type="hidden" name="token" value="[% token %]">
+ <input type="submit" name="Update statuses" value="[% loc('Update statuses') %]">
+ </p>
+ </form>
+
+ <h2>[% loc('Add new category') %]</h2>
+
+ <form method="post" action="[% c.uri_for('council_contacts', area_id ) %]" enctype="application/x-www-form-urlencoded" accept-charset="utf-8">
+
+ [% IF c.cobrand.moniker != 'emptyhomes' %]
+ <p>
+ <strong>[% loc('Category:') %] </strong><input type="text" name="category" size="30">
+ </p>
+ [% END %]
+
+ <p>
+ <strong>[% loc('Email:') %] </strong><input type="text" name="email" size="30">
+ </p>
+
+ <p>
+ <input type="checkbox" name="confirmed" value="1" id="confirmed">
+ <label for="confirmed">[% loc('Confirmed') %]</label>
+
+ <input type="checkbox" name="deleted" value="1"id="deleted">
+ <label for="deleted">[% loc('Deleted') %]</label>
+ </p>
+
+ <p>
+ <strong>[% loc('Note:') %] </strong> <textarea name="note" rows="3" cols="40"></textarea>
+ </p>
+
+ <p>
+ <input type="hidden" name="area_id" value="[% area_id %]" >
+ <input type="hidden" name="posted" value="new" >
+ <input type="hidden" name="token" value="[% token %]" >
+ <input type="submit" name="Create category" value="[% loc('Create category') %]" >
+ </p>
+
+ <div>
+ <input type="hidden" name=".cgifields" value="confirmed" >
+ <input type="hidden" name=".cgifields" value="deleted" >
+ </div>
+ </form>
+
+[% INCLUDE 'admin/footer.html' %]
diff --git a/templates/web/default/admin/council_contacts.txt b/templates/web/default/admin/council_contacts.txt
new file mode 100644
index 000000000..2d1e04bfa
--- /dev/null
+++ b/templates/web/default/admin/council_contacts.txt
@@ -0,0 +1,4 @@
+[% WHILE ( contact = contacts.next ) -%]
+[%- NEXT IF contact.deleted || ! contact.confirmed %]
+[% contact.category %] [% contact.email %]
+[%- END %]
diff --git a/templates/web/default/admin/council_edit.html b/templates/web/default/admin/council_edit.html
new file mode 100644
index 000000000..f6e820bab
--- /dev/null
+++ b/templates/web/default/admin/council_edit.html
@@ -0,0 +1,62 @@
+[% INCLUDE 'admin/header.html' title=tprintf(loc('Council contacts for %s'), council_name) -%]
+
+[% BLOCK highlightchanged_yesno %]
+[%- output = loc('No') %]
+[%- IF new.$value %][% output = loc('Yes') %][% END %]
+[%- IF old && old.$value != new.$value %]<strong>[% output %]</strong>[% ELSE %][% output %][% END %]
+[%- END %]
+
+[% BLOCK highlightchanged %]
+[%- IF old && old.$value != new.$value %]<strong>[% new.$value %]</strong>[% ELSE %][% new.$value %][% END %]
+[%- END %]
+<p>
+<em>[% updated %]</em>
+</p>
+
+<p>
+[% IF example_pc %]
+<a href="[% c.uri_for( '/around', { pc => example_pc } ) %]">[% tprintf( loc('Example postcode %s'), example_pc ) | html %]</a>
+[% END %]
+</p>
+
+<form method="post" action="[% c.uri_for('council_contacts', area_id ) %]" enctype="application/x-www-form-urlencoded" accept-charset="utf-8">
+ <strong>[% loc('Category:') %] </strong>[% contact.category | html %]
+ <input type="hidden" name="category" value="[% contact.category | html %]" >
+ <input type="hidden" name="token" value="[% token %]" >
+ <strong>[% loc('Email:') %] </strong>
+ <input type="text" name="email" value="[% contact.email | html %]" size="30">
+ <input type="checkbox" name="confirmed" value="1" id="confirmed"[% ' checked' IF contact.confirmed %]> <label for="confirmed">[% loc('Confirmed' ) %]</label>
+ <input type="checkbox" name="deleted" value="1" id="deleted"[% ' checked' IF contact.deleted %]> <label for="deleted">[% loc('Deleted') %]</label><br>
+
+ <strong>[% loc('Note:') %] </strong><textarea name="note" rows="3" cols="40">[% contact.note | html %]</textarea> <br>
+
+ <input type="hidden" name="area_id" value="[% area_id %]">
+ <input type="hidden" name="posted" value="new">
+ <input type="submit" name="Save changes" value="[% loc('Save changes') %]">
+</form>
+
+<h2>[% loc('History') %]</h2>
+<table border="1">
+ <tr>
+ <th>[% loc('When edited') %]</th>
+ <th>[% loc('Email') %]</th>
+ <th>[% loc('Confirmed') %]</th>
+ <th>[% loc('Deleted') %]</th>
+ <th>[% loc('Editor') %]</th>
+ <th>[% loc('Note') %]</th>
+ </tr>
+ [%- prev = '' %]
+ [%- WHILE ( contact = history.next ) %]
+ <tr>
+ <td>[% contact.whenedited.ymd _ ' ' _ contact.whenedited.hms %]</td>
+ <td>[% PROCESS highlightchanged old=prev new=contact value='email' %]</td>
+ <td>[% PROCESS highlightchanged_yesno old=prev new=contact value='confirmed' %]</td>
+ <td>[% PROCESS highlightchanged_yesno old=prev new=contact value='deleted' %]</td>
+ <td>[% contact.editor %]</td>
+ <td>[% contact.note | html %]</td>
+ </tr>
+ [%- prev = contact %]
+ [%- END %]
+</table>
+
+[% INCLUDE 'admin/footer.html' %]
diff --git a/templates/web/default/admin/council_list.html b/templates/web/default/admin/council_list.html
new file mode 100644
index 000000000..28aefbc3c
--- /dev/null
+++ b/templates/web/default/admin/council_list.html
@@ -0,0 +1,47 @@
+[% INCLUDE 'admin/header.html' title=loc('Council contacts') -%]
+
+[%- BLOCK details %]
+[%- IF councils.size == 0 %]
+ [%- loc('None') %]
+[%- ELSE %]
+[%- FOREACH council IN councils %]
+ [%- IF council.parent_area %]
+ [%-
+ p_area = areas.$council.parent_area
+ parent = ', ' _ areas.$parent.name
+ %]
+ [%- ELSE %]
+ [%- parent = '' %]
+ [%- END %]
+ [%- '<ul>' IF loop.first %]
+ <li><a href="[% c.uri_for( 'council_contacts', council ) %]">[% areas.$council.name %] [% parent %][% tprintf( loc('%d addresses'), counts.$council.c) IF counts.$council && c.cobrand.moniker != 'emptyhomes' %]</a>
+ [%- '</ul>' IF loop.last %]
+[%- END %]
+[%- END %]
+[%- END %]
+
+<h2>[% loc('Diligency prize league table') %]</h2>
+[% IF edit_activity.count %]
+<ul>
+ [% WHILE ( editor = edit_activity.next ) %]
+ <li>[% tprintf( loc('%d edits by %s'), editor.get_column('c'), editor.name ) %]</li>
+ [% END %]
+</ul>
+[% ELSE %]
+<p>
+[% loc('No edits have yet been made.') %]
+</p>
+[% END %]
+
+<h2>[% loc('Councils') %]</h2>
+
+<h3>[% loc('No info at all') %]</h3>
+[% PROCESS details councils=no_info %]
+<h3>[% loc('Currently has 1+ deleted') %]</h3>
+[% PROCESS details councils=one_plus_deleted %]
+<h3>[% loc('Some unconfirmeds') %]</h3>
+[% PROCESS details councils=unconfirmeds %]
+<h3>[% loc('All confirmed') %]</h3>
+[% PROCESS details councils=all_confirmed %]
+
+[% INCLUDE 'admin/footer.html' %]
diff --git a/templates/web/default/admin/footer.html b/templates/web/default/admin/footer.html
new file mode 100644
index 000000000..308b1d01b
--- /dev/null
+++ b/templates/web/default/admin/footer.html
@@ -0,0 +1,2 @@
+</body>
+</html>
diff --git a/templates/web/default/admin/header.html b/templates/web/default/admin/header.html
new file mode 100644
index 000000000..261f0efb2
--- /dev/null
+++ b/templates/web/default/admin/header.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+<title>[% title %] - [% loc('FixMyStreet administration') %]</title>
+<style type="text/css">
+dt { clear: left; float: left; font-weight: bold; }
+dd { margin-left: 8em; }
+.hidden { color: #666666; }
+</style>
+</head>
+<body>
+
+ <strong>[% loc('FixMyStreet admin:') %]</strong>
+ [%- FOREACH link IN allowed_links %]
+ <a href="[% c.uri_for( link ) %]">[% allowed_pages.$link.0 %]</a>
+ [%- END %]
+
+ <h1>[% title %]</h1>
diff --git a/templates/web/default/admin/index.html b/templates/web/default/admin/index.html
new file mode 100644
index 000000000..59722a5ed
--- /dev/null
+++ b/templates/web/default/admin/index.html
@@ -0,0 +1,31 @@
+[% INCLUDE 'admin/header.html' title=loc('Summary') -%]
+
+[%- BLOCK states -%]
+<h2>[% title %]</h2>
+
+[%- FOREACH state IN object.keys.sort %]
+[%- '<ul>' IF loop.first %]
+ <li>[% object.$state %] [% state %]</li>
+[%- "\n</ul>" IF loop.last %]
+[%- END %]
+[% END -%]
+
+ <ul>
+ <li>[% tprintf( loc('<strong>%d</strong> live problems'), total_problems_live ) %]</li>
+ <li>[% tprintf( loc('%d live updates'), comments.confirmed || 0 ) %]</li>
+ <li>[% tprintf( loc('%d confirmed alerts, %d unconfirmed'), alerts.1, alerts.0) %]</li>
+ <li>[% tprintf( loc('%d questionnaires sent &ndash; %d answered (%s%%)'), questionnaires.total, questionnaires.1, questionnaires_pc) %]</li>
+ <li>[% tprintf( loc('%d council contacts &ndash; %d confirmed, %d unconfirmed'), contacts.total, contacts.1, contacts.0) %]</li>
+ </ul>
+
+[% IF c.cobrand.admin_show_creation_graph -%]
+ <p>
+ <a href="">[% loc('Graph of problem creation by status over time') %]</a>
+ </p>
+[% END -%]
+
+[% PROCESS states title=loc('Problem breakdown by state') object=problems %]
+
+[% PROCESS states title=loc('Update breakdown by state') object=comments %]
+
+[% INCLUDE 'admin/footer.html' %]
diff --git a/templates/web/default/admin/list_updates.html b/templates/web/default/admin/list_updates.html
new file mode 100644
index 000000000..92f41f348
--- /dev/null
+++ b/templates/web/default/admin/list_updates.html
@@ -0,0 +1,34 @@
+<h2>[% loc('Updates') %]</h2>
+
+<table cellspacing="0" cellpadding="2" border="1">
+ <tr>
+ <th>[% loc('ID') %]</th>
+ <th>[% loc('State') %]</th>
+ <th>[% loc('Name') %]</th>
+ <th>[% loc('Email') %]</th>
+ <th>[% loc('Created') %]</th>
+ <th>[% loc('Anonymous') %]</th>
+ <th>[% loc('Cobrand') %]</th>
+ <th>[% loc('Text') %]</th>
+ <th>*</th>
+ </tr>
+[% FOREACH update IN updates -%]
+ <tr[% ' class="hidden"' IF update.state == 'hidden' || ( problem.state && problem.state == 'hidden' ) %]>
+ <td>[%- IF update.state == 'confirmed' -%]
+ [%- cobrand_data = update.cobrand_data %]
+ [%- cobrand_data = c.data_for_generic_update IF !update.cobrand %]
+ <a href="[% c.uri_for_email( '/report', update.problem.id, cobrand_data ) %]#update_[% update.id %]">[% update.id %]</a>
+ [%- ELSE %]
+ [%- update.id %]
+ [%- END -%]</td>
+ <td>[% update.state %]</td>
+ <td>[% update.name | html %]</td>
+ <td>[% update.user.email | html %]</td>
+ <td>[% PROCESS format_time time=update.created %]</td>
+ <td>[% IF update.anonymous %][% loc('Yes') %][% ELSE %][% loc('No') %][% END %]</td>
+ <td>[% update.cobrand %]<br>[% update.cobrand_data | html %]</td>
+ <td>[% update.text | html %]</td>
+ <td><a href="[% c.uri_for( 'update_edit', update.id ) %]">[% loc('Edit') %]</a></td>
+ <tr>
+[% END -%]
+</table>
diff --git a/templates/web/default/admin/questionnaire.html b/templates/web/default/admin/questionnaire.html
new file mode 100644
index 000000000..baeb7c800
--- /dev/null
+++ b/templates/web/default/admin/questionnaire.html
@@ -0,0 +1,21 @@
+[% INCLUDE 'admin/header.html' title=loc('Survey Results') %]
+
+<table border="1">
+ <tr>
+ <th>[% loc('Reported before') %]</th>
+ <th>[% loc('Not reported before') %]</th>
+ </tr>
+ [% IF questionnaires.total > 0 %]
+ <tr>
+ <td>[% questionnaires.1 %] ([% reported_pc %]%)</td>
+ <td>[% questionnaires.0 %] ([% not_reported_pc %]%)</td>
+ </tr>
+ [% ELSE %]
+ <tr>
+ <td>[% loc('n/a') %]</td>
+ <td>[% loc('n/a') %]</td>
+ </tr>
+ [% END %]
+</table>
+
+[% INCLUDE 'admin/footer.html' %]
diff --git a/templates/web/default/admin/report_blocks.html b/templates/web/default/admin/report_blocks.html
new file mode 100644
index 000000000..1fe650f15
--- /dev/null
+++ b/templates/web/default/admin/report_blocks.html
@@ -0,0 +1,7 @@
+[% BLOCK value_or_nbsp -%]
+ [%- IF value %][% value | html %][% ELSE %]&nbsp;[% END %]
+[%- END %]
+
+[% BLOCK format_time -%]
+ [%- IF time %][% time.ymd %]&nbsp;[% time.hms %][% ELSE %][% no_time || '&nbsp;' %][% END %][% no_time = '' %]
+[%- END %]
diff --git a/templates/web/default/admin/report_edit.html b/templates/web/default/admin/report_edit.html
new file mode 100644
index 000000000..ef083ca7e
--- /dev/null
+++ b/templates/web/default/admin/report_edit.html
@@ -0,0 +1,50 @@
+[% INCLUDE 'admin/header.html' title=tprintf(loc('Editing problem %d'), problem.id ) -%]
+[% PROCESS 'admin/report_blocks.html' %]
+
+[% status_message %]
+
+<form method="post" action="[% c.uri_for( 'report_edit', problem.id ) %]" enctype="application/x-www-form-urlencoded" accept-charset="utf-8">
+ <input type="hidden" name="token" value="[% token %]" >
+ <input type="hidden" name="submit" value="1" >
+<ul>
+ [%- cobrand_data = problem.cobrand_data %]
+ [%- cobrand_data = c.data_for_generic_problem IF !problem.cobrand %]
+<li><a href="[% c.uri_for_email( '/report', problem.id, cobrand_data ) %]">[% loc('View report on site' )%]</a></li>
+<li><label for='title'>[% loc('Subject:') %]</label> <input size=60 type='text' id='title' name='title' value='[% problem.title | html %]'></li>
+<li><label for='detail'>[% loc('Details:') %]</label><br><textarea name='detail' id='detail' cols=60 rows=10>[% problem.detail | html %]</textarea></li>
+<li>[% loc('Co-ordinates:') %] [% problem.latitude %], [% problem.longitude %] ( [% loc('originally entered') %] [% problem.postcode | html %] , [% IF problem.used_map %][% loc('used map') %][% ELSE %][% loc("didn't use map") %][% END %])</li>
+<li>[% loc('For council(s):') %] [% IF problem.council %][% problem.council %][% ELSE %]<em>[% loc('None' ) %]</em>[% END %] ([% loc('other areas:') %] [% problem.areas | remove('^,') | remove( ',$' ) %])</li>
+<li><label for="anonymous">[% loc('Anonymous:') %]</label> <select name="anonymous" id="anonymous">
+<option [% 'selected ' IF problem.anonymous %]value="1">[% loc('Yes') %]</option>
+<option [% 'selected ' IF !problem.anonymous %]value="0">[% loc('No') %]</option>
+</select></li>
+<li><label for="state">[% loc('State:') %]</label> <select name="state" id="state">
+ [% FOREACH state IN [ ['confirmed', loc('Open')], ['fixed', loc('Fixed')], ['hidden', loc('Hidden')], ['partial', loc('Partial')],['unconfirmed',loc('Unconfirmed')] ] %]
+ <option [% 'selected ' IF state.0 == problem.state %] value="[% state.0 %]">[% state.1 %]</option>
+ [% END %]
+</select></li>
+<li>[% loc('Category:') %] [% problem.category | html %] </li>
+<li>[% loc('Name:') %] <input type='text' name='name' id='name' value='[% problem.name | html %]'></li>
+<li>[% loc('Email:') %] <input type='text' id='email' name='email' value='[% problem.user.email | html %]'></li>
+<li>[% loc('Phone:') %] [% problem.user.phone | html %]</li>
+<li>[% loc('Created:') %] [% PROCESS format_time time=problem.created %]</li>
+<li>[% loc('Confirmed:') %] [% PROCESS format_time time=problem.confirmed no_time='-' %]</li>
+<li>[% loc('Sent:') %] [% PROCESS format_time time=problem.whensent %] [% IF problem.state == 'confirmed' %]<input onclick="return confirm('[% loc('You really want to resend?') %]')" type="submit" name="resend" value="[% loc('Resend report') %]">[% END %]</li>
+<li>[% loc('Last update:') %] [% PROCESS format_time time=problem.lastupdate %]</li>
+<li>[% loc('Service:') %] [% problem.service %]</li>
+<li>[% loc('Cobrand:') %] [% problem.cobrand %]</li>
+<li>[% loc('Cobrand data:') %] [% problem.cobrand_data %]</li>
+<li>[% loc('Going to send questionnaire?') %] [% IF problem.send_questionnaire %][% loc('Yes') %][% ELSE %][% loc('No') %][% END %]</li>
+
+[% IF problem.photo %]
+[% photo = problem.get_photo_params %]
+<li><img alt="" height="[% photo.height %]" width="[% photo.width %]" src="[% photo.url %]">
+<input type="checkbox" id="remove_photo" name="remove_photo" value="1">
+<label for="remove_photo">[% loc("Remove photo (can't be undone!)") %]</label></li>
+[% END %]
+</ul>
+<input type="submit" name="Submit changes" value="[% loc('Submit changes') %]" ></form>
+
+[% INCLUDE 'admin/list_updates.html' %]
+
+[% INCLUDE 'admin/footer.html' %]
diff --git a/templates/web/default/admin/search_reports.html b/templates/web/default/admin/search_reports.html
new file mode 100644
index 000000000..3809965f6
--- /dev/null
+++ b/templates/web/default/admin/search_reports.html
@@ -0,0 +1,61 @@
+[% INCLUDE 'admin/header.html' title=loc('Search Reports') %]
+[% PROCESS 'admin/report_blocks.html' %]
+
+<form method="get" action="[% c.uri_for('search_reports') %]" enctype="application/x-www-form-urlencoded" accept-charset="utf-8">
+ <label for="search">[% loc('Search:') %]</label> <input type="text" name="search" size="30" id="search">
+</form>
+
+
+[% IF searched %]
+<table cellspacing="0" cellpadding="2" border="1">
+ <tr>
+ <th>[% loc('ID') %]</th>
+ <th>[% loc('Title') %]</th>
+ <th>[% loc('Name') %]</th>
+ <th>[% loc('Email') %]</th>
+ <th>[% loc('Council') %]</th>
+ <th>[% loc('Category') %]</th>
+ <th>[% loc('Anonymous') %]</th>
+ <th>[% loc('Cobrand') %]</th>
+ <th>[% loc('Created') %]</th>
+ <th>[% loc('State') %]</th>
+ <th>[% loc('When sent') %]</th>
+ <th>*</th>
+ </tr>
+[%- FOREACH problem IN problems %]
+ <tr[% ' class="hidden"' IF problem.state == 'hidden' %]>
+ <td>[%- IF problem.state == 'confirmed' || problem.state == 'fixed' -%]
+ [%- cobrand_data = problem.cobrand_data %]
+ [%- cobrand_data = c.data_for_generic_problem IF !problem.cobrand %]
+ <a href="[% c.uri_for_email( '/report', problem.id, cobrand_data ) %]">[% problem.id %]</a>
+ [%- ELSE %]
+ [%- problem.id %]
+ [%- END -%]</td>
+ <td>[% PROCESS value_or_nbsp value=problem.title %]</td>
+ <td>[% PROCESS value_or_nbsp value=problem.name %]</td>
+ <td>[% PROCESS value_or_nbsp value=problem.user.email %]</td>
+ <td>[%- IF edit_council_contacts -%]
+ <a href="[% c.uri_for('council_contacts', problem.council ) %]">[% PROCESS value_or_nbsp value=problem.council %]</a>
+ [%- ELSE -%]
+ [%- PROCESS value_or_nbsp value=problem.council -%]
+ [%- END -%]</td>
+ <td>[% PROCESS value_or_nbsp value=problem.category %]</td>
+ <td>[% IF problem.anonymous %][% loc('Yes') %][% ELSE %][% loc('No') %][% END %]</td>
+ <td>[% problem.cobrand %]<br>[% problem.cobrand_data | html %]</td>
+ <td>[% PROCESS format_time time=problem.created %]</td>
+ <td>[% problem.state %]<small>
+ [%- IF problem.state == 'fixed' || problem.state == 'confirmed' %]<br>[% loc('Confirmed:' ) %]&nbsp;[% PROCESS format_time time=problem.confirmed %][% END -%]
+ [%- IF problem.state == 'fixed' %]<br>[% loc('Fixed:') %] [% PROCESS format_time time=problem.lastupdate %][% END -%]
+ [%- IF problem.state == 'confirmed' %]<br>[% loc('Last&nbsp;update:') %] [% PROCESS format_time time=problem.lastupdate %][% END -%]</small>
+ </td>
+ <td>[% PROCESS format_time time=problem.whensent %]</td>
+ <td><a href="[% c.uri_for( 'report_edit', problem.id ) %]">[% loc('Edit') %]</a></td>
+ </tr>
+[%- END -%]
+</table>
+
+[% INCLUDE 'admin/list_updates.html' %]
+
+[% END %]
+
+[% INCLUDE 'admin/footer.html' %]
diff --git a/templates/web/default/admin/timeline.html b/templates/web/default/admin/timeline.html
new file mode 100644
index 000000000..a7f4f3628
--- /dev/null
+++ b/templates/web/default/admin/timeline.html
@@ -0,0 +1,46 @@
+[% INCLUDE 'admin/header.html' title=loc('Timeline') %]
+[%- USE f = DateTime::Format('DateTime::Format::Strptime', { pattern => "%A, %e %B %Y" }) %]
+[%- USE alert_format = DateTime::Format('DateTime::Format::Strptime', { pattern => "%H:%M:%S %e %B %Y" }) %]
+
+[%- BLOCK problem_name %]
+ [%- tprintf(loc('by %s'), problem.name) | html %] &lt;[% problem.user.email | html %]&gt;, '[% problem.title | html %]'
+[%- END %]
+
+[%- date = '' %]
+[% FOREACH moment IN time.keys.sort.reverse %]
+ [%- curdate = f.format(time.$moment.0.date) -%]
+ [%- IF date != curdate %]
+ [% '</dl>' IF date %]
+ <h2>[% curdate %]</h2>
+
+ <dl>
+ [%- date = curdate -%]
+ [%- END -%]
+ <dt><b>[% time.$moment.0.date.hms %]</b></dt>
+ <dd>
+ [% FOREACH item IN time.$moment %]
+ [%- SWITCH item.type -%]
+ [% CASE 'problemCreated' %]
+ [%- tprintf(loc('Problem %d created'), item.obj.id) %]; [% PROCESS problem_name problem=item.obj -%]
+ [% CASE 'problemConfirmed' %]
+ [%- tprintf( loc('Problem %s confirmed'), '<a href="' _ c.uri_for_email( '/report', item.obj.id, c.cobrand_data ) _ '">' _ item.obj.id _ '</a>') %]; [% PROCESS problem_name problem=item.obj -%]
+ [% CASE 'problemSent' %]
+ [% tprintf(loc("Problem %s sent to council %s"), '<a href="' _ c.uri_for_email( '/report', item.obj.id, c.cobrand_data ) _ '">' _ item.obj.id _ '</a>', item.obj.council ) %]
+ [% CASE 'quesSent' %]
+ [% tprintf(loc("Questionnaire %d sent for problem %d"), item.obj.id, item.obj.problem_id ) %]
+ [% CASE 'quesAnswered' %]
+ [% tprintf(loc("Questionnaire %d answered for problem %d, %s to %s"), item.obj.id, item.obj.problem_id, item.obj.old_state, item.obj.new_state ) %]
+ [% CASE 'update' %]
+ [% name = ( item.obj.name || 'anonymous' ) | html %]
+ [% tprintf(loc("Update %s created for problem %d; by %s"), "<a href='" _ c.uri_for_email( '/report', item.obj.problem_id, c.cobrand_data ) _ "#update_" _ item.obj.id _ "'>" _ item.obj.id _ "</a>", item.obj.problem_id, name) %]&lt;[% item.obj.user.email | html %]&gt;
+ [% CASE 'alertSub' %]
+ [% tprintf(loc("Alert %d created for %s, type %s, parameters %s / %s"), item.obj.id, item.obj.user.email, item.obj.alert_type.ref, item.obj.parameter, item.obj.parameter2) | html %]
+ [% CASE 'alertDel' %]
+ [% tprintf(loc("Alert %d disabled (created %s)"), item.obj.id, alert_format.format( item.obj.whensubscribed_local ) ) %]
+ [%- END %]
+ <br />
+ [%- END %]
+ </dd>
+[% END %]
+
+[% INCLUDE 'admin/footer.html' %]
diff --git a/templates/web/default/admin/update_edit.html b/templates/web/default/admin/update_edit.html
new file mode 100644
index 000000000..d7f212052
--- /dev/null
+++ b/templates/web/default/admin/update_edit.html
@@ -0,0 +1,38 @@
+[% INCLUDE 'admin/header.html' title=tprintf(loc('Editing update %d'), update.id ) -%]
+[% PROCESS 'admin/report_blocks.html' %]
+
+[% status_message %]
+
+<form method="post" action="[% c.uri_for( 'update_edit', update.id ) %]" enctype="application/x-www-form-urlencoded" accept-charset="utf-8">
+ <input type="hidden" name="token" value="[% token %]" >
+ <input type="hidden" name="submit" value="1" >
+<ul>
+ [%- cobrand_data = update.cobrand_data %]
+ [%- cobrand_data = c.data_for_generic_update IF !update.cobrand %]
+<li><a href="[% c.uri_for_email( '/report', update.problem_id, cobrand_data ) %]#update_[% update.id %]">[% loc('View report on site' )%]</a></li>
+<li><label for='detail'>[% loc('Text:') %]</label><br><textarea name='text' id='text' cols=60 rows=10>[% update.text | html %]</textarea></li>
+<li><label for="anonymous">[% loc('Anonymous:') %]</label> <select name="anonymous" id="anonymous">
+<option [% 'selected ' IF update.anonymous %]value="1">[% loc('Yes') %]</option>
+<option [% 'selected ' IF !update.anonymous %]value="0">[% loc('No') %]</option>
+</select></li>
+<li><label for="state">[% loc('State:') %]</label> <select name="state" id="state">
+ [% FOREACH state IN [ ['confirmed', loc('Open')], ['hidden', loc('Hidden')], ['unconfirmed',loc('Unconfirmed')] ] %]
+ <option [% 'selected ' IF state.0 == update.state %] value="[% state.0 %]">[% state.1 %]</option>
+ [% END %]
+</select></li>
+<li>[% loc('Name:') %] <input type='text' name='name' id='name' value='[% update.name | html %]'></li>
+<li>[% loc('Email:') %] <input type='text' id='email' name='email' value='[% update.user.email | html %]'></li>
+<li>[% loc('Cobrand:') %] [% update.cobrand %]</li>
+<li>[% loc('Cobrand data:') %] [% update.cobrand_data %]</li>
+<li>[% loc('Created:') %] [% PROCESS format_time time=update.created %]</li>
+
+[% IF update.photo %]
+[% photo = update.get_photo_params %]
+<li><img alt="" height="[% photo.height %]" width="[% photo.width %]" src="[% photo.url %]">
+<input type="checkbox" id="remove_photo" name="remove_photo" value="1">
+<label for="remove_photo">[% loc("Remove photo (can't be undone!)") %]</label></li>
+[% END %]
+</ul>
+<input type="submit" name="Submit changes" value="[% loc('Submit changes') %]" ></form>
+
+[% INCLUDE 'admin/footer.html' %]
diff --git a/templates/web/default/alert/choose.html b/templates/web/default/alert/choose.html
new file mode 100644
index 000000000..ef632e2d1
--- /dev/null
+++ b/templates/web/default/alert/choose.html
@@ -0,0 +1,14 @@
+[% INCLUDE 'header.html', title => loc('Local RSS feeds and email alerts') %]
+
+<h1>[% loc('Local RSS feeds and email alerts') %]</h1>
+
+[% IF possible_location_matches %]
+ <p>[% loc('We found more than one match for that location. We show up to ten matches, please try a different search if yours is not here.') %]</p>
+ <ul class="pc_alternatives">
+ [% FOREACH match IN possible_location_matches %]
+ <li><a href="[% choose_target_uri %]?pc=[% match | uri %]">[% match | html %]</a></li>
+ [% END %]
+ </ul>
+[% END %]
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/alert/index.html b/templates/web/default/alert/index.html
new file mode 100644
index 000000000..4d63e7f34
--- /dev/null
+++ b/templates/web/default/alert/index.html
@@ -0,0 +1,39 @@
+[% INCLUDE 'header.html', title => loc('Local RSS feeds and email alerts') %]
+
+<h1>[% loc('Local RSS feeds and email alerts') %]</h1>
+
+<p>
+[% loc('FixMyStreet has a variety of RSS feeds and email alerts for local problems, including
+alerts for all problems within a particular ward or council, or all problems
+within a certain distance of a particular location.') %]
+</p>
+
+[% IF location_offshore %]
+ <ul class="error"><li>[% loc('That location does not appear to be covered by a council, perhaps it is offshore - please try somewhere more specific.') %]</li></ul>
+[% ELSIF location_error %]
+ <ul class="error"><li>[% location_error | html %]</li></ul>
+[% ELSE %]
+ [% INCLUDE 'errors.html' %]
+[% END %]
+
+<form method="get" action="/alert/list">
+ <p>
+ [% loc('To find out what local alerts we have for you, please enter your GB
+postcode or street name and area:' ) %]
+ <input type="text" name="pc" value="[% pc | html %]">
+ <input type="submit" value="[% loc('Go') %]">
+ [% cobrand_form_elements %]
+ </p>
+</form>
+
+[% IF photos.size %]
+<div id="alert_recent">
+ <h2>[% loc('Some photos of recent reports') %]</h2>
+ [% FOREACH p IN photos %]
+ <a href="/report/[% p.id %]"><img border="0" height="100"
+ src="/photo?tn=1;id=[% p.id %]" alt="[% p.title | html %]" title="[% p.title | html %]"></a>
+ [% END %]
+</div>
+[% END %]
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/alert/list.html b/templates/web/default/alert/list.html
new file mode 100644
index 000000000..38007ea62
--- /dev/null
+++ b/templates/web/default/alert/list.html
@@ -0,0 +1,129 @@
+[%
+ IF pretty_pc;
+ title = tprintf( loc("Local RSS feeds and email alerts for ‘%s’"), pretty_pc );
+ ELSE;
+ title = loc('Local RSS feeds and email alerts');
+ END;
+%]
+
+[% INCLUDE 'header.html', title => title %]
+
+[% IF pretty_pc %]
+ [%
+ pretty_pc = pretty_pc | html | replace(' ', '&nbsp;');
+ title = tprintf( loc("Local RSS feeds and email alerts for ‘%s’"), pretty_pc );
+ %]
+[% END %]
+
+
+<h1>[% title %]</h1>
+
+<form id="alerts" name="alerts" method="post" action="/alert/subscribe">
+ <input type="hidden" name="type" value="local">
+ <input type="hidden" name="pc" value="[% pc | html %]">
+
+ [% cobrand_form_elements %]
+
+ [% IF photos.size %]
+ <div id="alert_photos">
+ <h2>[% loc('Photos of recent nearby reports') %]</h2>
+ [% FOREACH p IN photos %]
+ <a href="/report/[% p.id %]"><img border="0" height="100"
+ src="/photo?tn=1;id=[% p.id %]" alt="[% p.title | html %]" title="[% p.title | html %]"></a>
+ [% END %]
+ </div>
+ [% END %]
+
+ <p>
+ [% IF pretty_pc %]
+ [% tprintf( loc('Here are the types of local problem alerts for &lsquo;%s&rsquo;.'), pretty_pc ) %]
+ [% END %]
+ [% loc('Select which type of alert you\'d like and click the button for an RSS feed, or enter your email address to subscribe to an email alert.') %]
+ </p>
+
+ [% INCLUDE 'errors.html' %]
+
+ <p>
+ [% loc('The simplest alert is our geographic one:') %]
+ </p>
+
+ <p id="rss_local">
+ <input type="radio" name="feed" id="[% rss_feed_id %]" value="[% rss_feed_id %]"[% IF rss_feed_id == selected_feed || selected_feed == '' %] checked[% END %]>
+ <label for="[% rss_feed_id %]">[% tprintf( loc('Problems within %.1fkm of this location'), population_radius ) %]</label>
+ [% loc('(a default distance which covers roughly 200,000 people)') %] <a href='[% rss_feed_uri %]'>
+ <img src='/i/feed.png' width='16' height='16' title='[% loc('RSS feed of nearby problems') %]' alt='[% loc('RSS feed') %]' border='0'></a>
+ </p>
+
+ <p id="rss_local_alt">
+ [% loc('(alternatively the RSS feed can be customised, within') %]
+ <a href="[% rss_feed_2k %]">2km</a> / <a href="[% rss_feed_5k %]">5km</a> / <a href="[% rss_feed_10k %]">10km</a> / <a href="[% rss_feed_20k %]">20km</a>)
+ </p>
+
+ <p>
+ [% loc("Or you can subscribe to an alert based upon what ward or council you&rsquo;re in:") %]
+ </p>
+
+ [% IF reported_to_options %]
+ <div id="rss_list">
+ <p><strong>
+ [% loc('Problems within the boundary of:') %]
+ </strong></p>
+ <ul>
+ [% ELSE %]
+ <div><ul id="rss_feed">
+ [% END %]
+
+ [% FOREACH option IN options %]
+ <li>
+ <input type="radio" name="feed" id="[% option.id %]" value="[% option.id %]"[% IF option.id == selected_feed %] checked[% END %]>
+ <label for="[% option.id %]">[% option.text %]</label>
+ <a href="[% option.uri %]"><img src="/i/feed.png" width="16" height="16"
+title="[% option.rss_text %]" alt="RSS feed" border="0"></a>
+ </li>
+ [% END %]
+</ul>
+ [% IF reported_to_options %]
+ <p><strong>
+ [% loc('Or problems reported to:') %]
+ </strong></p>
+ <ul>
+ [% FOREACH option IN reported_to_options %]
+ <li>
+ <input type="radio" name="feed" id="[% option.id %]" value="[% option.id %]"[% IF option.id == selected_feed %] checked[% END %]>
+ <label for="[% option.id %]">[% option.text %]</label>
+ <a href="[% option.uri %]"><img src="/i/feed.png" width="16" height="16"
+ title="[% option.rss_text %]" alt="RSS feed" border="0"></a>
+ </li>
+ [% END %]
+ </ul>
+ <p><small>
+ [% loc( 'FixMyStreet sends different categories of problem
+to the appropriate council, so problems within the boundary of a particular council
+might not match the problems sent to that council. For example, a graffiti report
+will be sent to the district council, so will appear in both of the district
+council&rsquo;s alerts, but will only appear in the "Within the boundary" alert
+for the county council.' ) %]
+ </small></p>
+</div>
+<div id="rss_buttons">
+ [% END %]
+
+ <p>
+ <input type="submit" name="rss" value="[% loc('Give me an RSS feed') %]">
+ <p>
+
+ <p id="alert_or">
+ [% loc('or') %]
+ </p>
+
+ <p>
+ [% loc('Your email:') %] <input type="text" id="rznvy" name="rznvy" value="[% rznvy | html %]" size="30">
+ </p>
+
+ <p>
+ <input type="submit" name="alert" value="[% loc('Subscribe me to an email alert') %]">
+ </p>
+ </div> <!-- ???? -->
+ </form>
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/alert/updates.html b/templates/web/default/alert/updates.html
new file mode 100644
index 000000000..88b014ff6
--- /dev/null
+++ b/templates/web/default/alert/updates.html
@@ -0,0 +1,22 @@
+[% title = loc('Local RSS feeds and email alerts') %]
+
+[% INCLUDE 'header.html', title => title %]
+
+
+[% INCLUDE 'errors.html' %]
+
+<p>
+[% loc('Receive email when updates are left on this problem.') %]
+</p>
+
+<form action="/alert/subscribe" method="post">
+<label class="n" for="alert_rznvy">[% loc('Email:') %]</label>
+<input type="text" name="rznvy" id="alert_rznvy" value="[% email | html %]" size="30">
+<input type="hidden" name="id" value="[% problem_id | html %]">
+<input type="hidden" name="type" value="updates">
+<input type="submit" value="[% loc('Subscribe') %]">
+[% cobrand_form_elements %]
+</form>
+
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/around/around_index.html b/templates/web/default/around/around_index.html
new file mode 100644
index 000000000..8c144e469
--- /dev/null
+++ b/templates/web/default/around/around_index.html
@@ -0,0 +1,50 @@
+[% INCLUDE 'header.html', title => loc('Reporting a problem') %]
+
+[%
+ # NOTE ON PARTIAL REPORTS:
+ #
+ # partial reports get a bit of extra text added, the form goes to
+ # '/report/new' and the partial hidden field is added to the form.
+%]
+
+[%
+ question
+ = c.cobrand.enter_postcode_text()
+ || loc("Enter a nearby GB postcode, or street name and area");
+%]
+
+<form action="[% c.uri_for('/around') %]" method="get" name="postcodeForm" id="postcodeForm">
+ <label for="pc">[% question %]:</label>&nbsp;<input type="text" name="pc" value="[% pc | html %]" id="pc" size="10" maxlength="200">&nbsp;<input type="submit" value="[% loc('Go') %]" id="submit">
+[% c.cobrand.form_elements('postcodeForm') %]
+
+[% IF partial_token %]
+ <input type="hidden" name="partial" value="[% partial_token.token %]">
+[% END %]
+
+</form>
+
+[% IF location_offshore %]
+ <p class="error">[% loc('That spot does not appear to be covered by a council. If you have tried to report an issue past the shoreline, for example, please specify the closest point on land.') %]</p>
+[% ELSIF location_error %]
+ <p class="error">[% location_error %]</p>
+[% END %]
+
+[% IF possible_location_matches %]
+ <p>[% loc('We found more than one match for that location. We show up to ten matches, please try a different search if yours is not here.') %]</p>
+ <ul class="pc_alternatives">
+ [% FOREACH match IN possible_location_matches %]
+ <li><a href="/around?pc=[% match | uri %]">[% match | html %]</a></li>
+ [% END %]
+ </ul>
+[% END %]
+
+[% IF partial_token %]
+ <p style="margin-top: 0; color: #cc0000;">
+ <img align="right" src="/photo?id=[% report.id %]" hspace="5">
+ [% loc("Thanks for uploading your photo. We now need to locate your problem, so please enter a nearby street name or postcode in the box below&nbsp;:") %]
+ </p>
+[% END %]
+
+
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/around/around_map_list_items.html b/templates/web/default/around/around_map_list_items.html
new file mode 100644
index 000000000..e13e9ccc9
--- /dev/null
+++ b/templates/web/default/around/around_map_list_items.html
@@ -0,0 +1,16 @@
+[% IF around_map.size %]
+ [% FOREACH p IN around_map %]
+
+ [% dist = tprintf("%.1f", (p.distance || 0) ) %]
+
+ <li>
+ <a href="[% c.uri_for('/report', p.problem.id ) %]">[% p.problem.title | html %]</a>
+ <small>([% prettify_epoch( p.problem.confirmed_local.epoch, 1 ) %], [% dist %]km)</small>
+ [% IF p.problem.state == 'fixed' %]
+ <small>[% loc('(fixed)') %]</small>
+ [% END %]
+ </li>
+ [% END %]
+[% ELSE %]
+ <li>[% loc('No problems found.') %]</li>
+[% END %]
diff --git a/templates/web/default/around/display_location.html b/templates/web/default/around/display_location.html
new file mode 100755
index 000000000..4c728806b
--- /dev/null
+++ b/templates/web/default/around/display_location.html
@@ -0,0 +1,128 @@
+[%
+
+ rss_alt = loc('RSS feed');
+ rss_title = loc('RSS feed of recent local problems');
+
+ rss_url
+ = pc
+ ? c.uri_for( "/rss/pc", pc )
+ : c.uri_for( "/rss/l/$short_latitude,$short_longitude" );
+
+ email_url = c.uri_for(
+ '/alert/list',
+ {
+ lat => short_latitude,
+ lon => short_longitude,
+ feed => "local:$short_latitude:$short_longitude",
+ }
+ );
+
+ url_skip = c.uri_for(
+ '/report/new',
+ {
+ pc => pc
+ latitude => short_latitude,
+ longitude => short_longitude,
+ submit_map => 1,
+ skipped => 1,
+ }
+ );
+
+ PROCESS "maps/${map.type}.html";
+
+ INCLUDE 'header.html',
+ title => loc('Viewing a location')
+ rss => [ loc('Recent local problems, FixMyStreet'), rss_url ],
+ robots => 'noindex,nofollow';
+%]
+
+<form action="[% c.uri_for('/report/new') %]" method="post" name="mapForm" id="mapForm">
+<input type="hidden" name="submit_map" value="1">
+<input type="hidden" name="map" value="[% c.req.params.map | html %]">
+<input type="hidden" name="pc" value="[% pc | html %]">
+[% c.cobrand.form_elements('mapForm') %]
+
+<input type="hidden" name="latitude" id="fixmystreet.latitude" value="[% short_latitude | html %]">
+<input type="hidden" name="longitude" id="fixmystreet.longitude" value="[% short_longitude | html %]">
+
+[% map_html %]
+
+<p id='sub_map_links'>
+ [% IF c.req.params.no_pins %]
+ <a id='hide_pins_link' rel='nofollow' href='[% c.uri_with( { no_pins => 0 } ) %]'>[% loc('Show pins') %]</a>
+ [% ELSE %]
+ <a id='hide_pins_link' rel='nofollow' href='[% c.uri_with( { no_pins => 1 } ) %]'>[% loc('Hide pins') %]</a>
+ [% END %]
+ [% IF c.config.COUNTRY == 'GB' %]
+ |
+ [% IF c.req.params.all_pins %]
+ <a id='all_pins_link' rel='nofollow' href='[% c.uri_with( { no_pins => undef, all_pins => undef } ) %]'>[% loc('Hide stale reports') %]</a>
+ [% ELSE %]
+ <a id='all_pins_link' rel='nofollow' href='[% c.uri_with( { no_pins => undef, all_pins => 1 } ) %]'>[% loc('Include stale reports') %]</a>
+ [% END %]
+ [% END %]
+</p>
+
+[% IF c.config.COUNTRY == 'GB' %]
+<input type='hidden' id='all_pins' name='all_pins' value='[% all_pins | html %]'>
+[% END %]
+
+</div>
+<div id="side">
+
+<h1>[% loc('Problems in this area') %]</h1>
+
+<p id="alert_links_area">
+ <a id="email_alert" rel="nofollow" href="[% email_url | html %]">
+ [%- loc('Email me new local problems') -%]
+</a> |
+ <a href="[% rss_url | html %]" id="rss_alert">
+ <span>[% rss_alt %]</span>
+ <img src="/i/feed.png" width="16" height="16" title="[% rss_title %]" alt="[% rss_alt %]" border="0" style="vertical-align: top">
+ </a>
+</p>
+
+[% IF location_error %]
+ <ul class="error">
+ <li>[% location_error | html %]</li>
+ </ul>
+[% END %]
+
+<p id="text_map">
+ [% loc( 'To report a problem, simply <strong>click on the map</strong> at the correct location.' ) %]
+ [%
+ tprintf(
+ loc("<small>If you cannot see the map, <a href='%s' rel='nofollow'>skip this step</a>.</small>"),
+ url_skip
+ )
+ %]
+</p>
+
+<div id="nearby_lists">
+
+ <h2>[% loc('Reports on and around the map') %]</h2>
+
+ <ul id="current">
+ [% INCLUDE "around/on_map_list_items.html" %]
+ </ul>
+
+
+ <h2 id="closest_problems">
+ [%
+ tprintf(
+ loc( 'Closest nearby problems <small>(within&nbsp;%skm)</small>' ),
+ distance
+ )
+ %]
+ </h2>
+
+ <ul id="current_near">
+ [% INCLUDE "around/around_map_list_items.html" %]
+ </ul>
+
+</div>
+
+</div>
+</form>
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/around/on_map_list_items.html b/templates/web/default/around/on_map_list_items.html
new file mode 100644
index 000000000..9db3d9492
--- /dev/null
+++ b/templates/web/default/around/on_map_list_items.html
@@ -0,0 +1,13 @@
+[% IF on_map.size %]
+ [% FOREACH p IN on_map %]
+ <li>
+ <a href="[% c.uri_for('/report', p.id ) %]">[% p.title | html %]</a>
+ <small>([% prettify_epoch( p.confirmed_local.epoch, 1 ) %])</small>
+ [% IF p.state == 'fixed' %]
+ <small>[% loc('(fixed)') %]</small>
+ [% END %]
+ </li>
+ [% END %]
+[% ELSE %]
+ <li>[% loc('No problems have been reported yet.') %]</li>
+[% END %]
diff --git a/templates/web/default/auth/change_password.html b/templates/web/default/auth/change_password.html
new file mode 100644
index 000000000..d4a7f107b
--- /dev/null
+++ b/templates/web/default/auth/change_password.html
@@ -0,0 +1,41 @@
+[% INCLUDE 'header.html', title => loc('Change Password') %]
+
+<h1>[% loc('Change Password') %]</h1>
+
+[% IF password_changed %]
+ <p>Your password has been changed!</p>
+[% END %]
+
+
+<form action="[% c.uri_for('change_password') %]" method="post" name="change_password">
+
+ [% IF password_error;
+
+ errors = {
+ missing => loc('Please enter a password'),
+ mismatch => loc('The passwords do not match'),
+ other => loc('Please check the passwords and try again'),
+ };
+
+ loc_password_error = errors.$password_error || errors.other;
+ END %]
+
+
+ <div>
+ <span class="error">[% loc_password_error %]</span><br>
+ <label for="new_password">[% loc('Password:') %]</label>
+ <input type="password" name="new_password" value="[% new_password | html %]">
+ <br>
+
+ <label for="confirm">[% loc('Again:') %]</label>
+ <input type="password" name="confirm" value="[% confirm | html %]">
+ <br>
+
+ <label for="login">&nbsp;</label>
+ <input type="submit" value="[% loc('Change Password') %]">
+ </div>
+
+</form>
+
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/auth/general.html b/templates/web/default/auth/general.html
new file mode 100644
index 000000000..32ea03177
--- /dev/null
+++ b/templates/web/default/auth/general.html
@@ -0,0 +1,53 @@
+[% INCLUDE 'header.html', title => loc('Login or create an account') %]
+
+<h1>[% loc('Login or create an account') %]</h1>
+
+
+<form action="[% c.uri_for() %]" method="post" name="general_auth">
+
+ [% IF email_error;
+
+ # other keys include fqdn, mxcheck if you'd like to write a custom error message
+
+ errors = {
+ missing => loc('Please enter your email'),
+ other => loc('Please check your email address is correct')
+ };
+
+ loc_email_error = errors.$email_error || errors.other;
+ END %]
+
+
+ <div>
+
+ [% IF loc_email_error %]
+ <span class="error">[% loc_email_error %]</span><br>
+ [% ELSIF login_error %]
+ <span class="error">Email or password wrong - please try again.</span><br>
+ [% END %]
+
+ <label for="email">[% loc('Email:') %]</label>
+ <input type="text" name="email" value="[% email || '' | html %]">
+ <br>
+
+ <label for="password">[% loc('Password:') %]</label>
+ <input type="password" name="password" value="">
+ <br>
+
+ <label for="remember_me">&nbsp;</label>
+ <input type="checkbox" name="remember_me" value='1' [% 'checked="checked"' IF remember_me %]>
+ Remember me - do not use on a public computer
+ <br>
+
+ <label for="login">&nbsp;</label>
+ <input type="submit" name="login" value="[% loc('Log me in') %]">
+
+ <h3>I don't have an account, or I've forgotten my password...</h3>
+ <label for="email_login">&nbsp;</label>
+ <input type="submit" name="email_login" value="[% loc('Email the details I need to the address I entered above') %]">
+ </div>
+
+</form>
+
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/auth/logout.html b/templates/web/default/auth/logout.html
new file mode 100644
index 000000000..9f3390f0a
--- /dev/null
+++ b/templates/web/default/auth/logout.html
@@ -0,0 +1,8 @@
+[% INCLUDE 'header.html', title => loc('Logout') %]
+
+<h1>[% loc('You have been logged out') %]</h1>
+
+<p>Please feel free to <a href="[% c.uri_for('/auth/') %]">login again</a>.</p>
+
+
+[% INCLUDE 'footer.html' %] \ No newline at end of file
diff --git a/templates/web/default/auth/token.html b/templates/web/default/auth/token.html
new file mode 100644
index 000000000..b3a3d5cc8
--- /dev/null
+++ b/templates/web/default/auth/token.html
@@ -0,0 +1,25 @@
+[% INCLUDE 'header.html', title => loc('Confirm account') %]
+
+[% IF token_not_found %]
+
+<h1>[% loc('Error') %]</h1>
+
+<p>We have not been able to confirm your account - sorry. This may be because:</p>
+
+<ul>
+ <li>Link too old or already used</li>
+ <li>URL not copied correctly</li>
+ [%# FIXME - add more reasons here %]
+</ul>
+
+[% ELSE %]
+
+<h1>[% loc('Please check your email') %]</h1>
+
+<p>We have sent you an email containing a link to confirm your account.</p>
+
+<p>If you do not receive the email in the next few minutes please check your spam folder.</p>
+
+[% END %]
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/common_header_tags.html b/templates/web/default/common_header_tags.html
new file mode 100644
index 000000000..e69dac27d
--- /dev/null
+++ b/templates/web/default/common_header_tags.html
@@ -0,0 +1,18 @@
+<meta http-equiv="content-type" content="text/html; charset=utf-8">
+<script type="text/javascript" src="/yui/utilities.js"></script>
+<script type="text/javascript" src="/js/fixmystreet.js"></script>
+
+[% map_js %]
+
+[% IF robots %]
+ <meta name="robots" content="[% robots %]">
+[% END %]
+
+[% IF rss %]
+ <link rel="alternate" type="application/rss+xml" title="[% rss.0 %]" href="[% rss.1 %]">
+[% END %]
+
+<title>
+ [% "$title :: " | html IF title %]
+ [% c.cobrand.site_title %]
+</title>
diff --git a/templates/web/default/contact/address.html b/templates/web/default/contact/address.html
new file mode 100644
index 000000000..b7ff37e5c
--- /dev/null
+++ b/templates/web/default/contact/address.html
@@ -0,0 +1,11 @@
+<div class="contact-details">
+<p>FixMyStreet is a service provided by mySociety, which is the project of a
+registered charity. The charity is called UK Citizens Online Democracy and is charity number 1076346.</p>
+<p>mySociety can be contacted by email at <a href="mailto:hello&#64;mysociety.org">hello&#64;mysociety.org</a>,
+or by post at:</p>
+<p>mySociety<br>
+483 Green Lanes<br>
+London<br>
+N13 4BS<br>
+UK</p>
+</div>
diff --git a/templates/web/default/contact/blurb.html b/templates/web/default/contact/blurb.html
new file mode 100644
index 000000000..22c9a3cef
--- /dev/null
+++ b/templates/web/default/contact/blurb.html
@@ -0,0 +1,9 @@
+<p>
+[% loc('Please do <strong>not</strong> report problems through this form; messages go to
+the team behind FixMyStreet, not a council. To report a problem,
+please <a href="/">go to the front page</a> and follow the instructions.') %]
+</p>
+
+<p>
+[% tprintf( loc("We'd love to hear what you think about this site. Just fill in the form, or send an email to <a href='mailto:%s'>%s</a>:"), contact_email, contact_email) %]
+</p>
diff --git a/templates/web/default/contact/index.html b/templates/web/default/contact/index.html
new file mode 100644
index 000000000..dc64dd554
--- /dev/null
+++ b/templates/web/default/contact/index.html
@@ -0,0 +1,101 @@
+[% INCLUDE 'header.html',
+ title = loc('Contact Us')
+ robots = 'noindex,nofollow'
+%]
+
+<h1>[% loc('Contact the team') %]</h1>
+
+<form method="post" action="/contact/submit">
+
+[% INCLUDE 'errors.html' %]
+
+[% IF update %]
+
+ <p>
+ [% loc('You are reporting the following update for being abusive, containing personal information, or similar:') %]
+ </p>
+
+ <blockquote>
+ <p>
+ [% IF update.anonymous %]
+ [% tprintf( loc('Update below added anonymously at %s'), prettify_epoch( update.confirmed_local.epoch ) ) %]
+ [% ELSE %]
+ [% tprintf( loc('Update below added by %s at %s'), update.name, prettify_epoch( update.confirmed_local.epoch ) ) | html %]
+ [% END %]
+ </p>
+
+ <p>
+ [% update.text | html %]
+ </p>
+
+ </blockquote>
+ <input type="hidden" name="update_id" value="[% update.id %]">
+ <input type="hidden" name="id" value="[% update.problem_id %]">
+
+[% ELSIF problem %]
+ <p>
+ [% loc('You are reporting the following problem report for being abusive, containing personal information, or similar:') %]
+ </p>
+
+ <blockquote>
+ <h2>[% problem.title | html %]</h2>
+
+ <p>
+ [% IF problem.anonymous %]
+ [% tprintf( loc('Reported anonymously at %s'), prettify_epoch( problem.confirmed_local.epoch ) ) %]
+ [% ELSE %]
+ [% tprintf( loc('Reported by %s at %s'), problem.user.name, prettify_epoch( problem.confirmed_local.epoch ) ) | html %]
+ [% END %]
+ </p>
+
+ <p>
+ [% problem.detail | html %]
+ </p>
+
+ </blockquote>
+ <input type="hidden" name="id" value="[% problem.id %]">
+
+[% ELSE %]
+
+ [% INCLUDE 'contact/blurb.html' %]
+
+[% END %]
+
+[% IF field_errors.name %]
+ <div class="form-error">[% field_errors.name %]</div>
+[% END %]
+<div class="form-field">
+<label for="form_name">[% loc('Your name:') %]</label>
+<input type="text" name="name" id="form_name" value="[% form_name | html %]" size="30"></div>
+
+
+[% IF field_errors.em %]
+ <div class="form-error">[% field_errors.em %]</div>
+[% END %]
+<div class="form-field">
+<label for="form_email">[% loc('Your&nbsp;email:') %]</label>
+<input type="text" name="em" id="form_email" value="[% em | html %]" size="30"></div>
+
+[% IF field_errors.subject %]
+ <div class="form-error">[% field_errors.subject %]</div>
+[% END %]
+<div class="form-field">
+<label for="form_subject">[% loc('Subject:') %]</label>
+<input type="text" name="subject" id="form_subject" value="[% subject | html %]" size="30"></div>
+
+[% IF field_errors.message %]
+ <div class="form-error">[% field_errors.message %]</div>
+[% END %]
+<div class="form-field">
+<label for="form_message">[% loc('Message:') %]</label>
+
+<textarea name="message" id="form_message" rows="7" cols="50">[% message | html %]</textarea></div>
+<div class="checkbox"><input type="submit" value="[% loc('Post') %]"></div>
+
+[% c.cobrand.form_elements('contactForm') %]
+
+</form>
+
+[% INCLUDE 'contact/address.html' %]
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/contact/submit.html b/templates/web/default/contact/submit.html
new file mode 100644
index 000000000..3845e9210
--- /dev/null
+++ b/templates/web/default/contact/submit.html
@@ -0,0 +1,20 @@
+[% INCLUDE 'header.html', title = loc('Contact Us') %]
+
+<h1>[% loc('Contact the team') %]</h1>
+
+[% IF success %]
+
+ <p>
+ [% loc("Thanks for your feedback. We'll get back to you as soon as we can!") %]
+ </p>
+ [% display_crosssell_advert( em, form_name, 'emailunvalidated', 1 ) %]
+
+[% ELSE %]
+
+ <p>
+ [% tprintf( loc('Failed to send message. Please try again, or <a href="mailto:%s">email us</a>.'), contact_email ) %]
+ </p>
+
+[% END %]
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/debug_footer.html b/templates/web/default/debug_footer.html
new file mode 100644
index 000000000..ffb3abbab
--- /dev/null
+++ b/templates/web/default/debug_footer.html
@@ -0,0 +1,40 @@
+[% IF c.config.STAGING_SITE %]
+
+<hr style="clear: both;">
+
+<ul>
+ <li>cobrand.moniker: [% c.cobrand.moniker %]</li>
+ <li>additional_template_paths: [% additional_template_paths.join(', ') || '--empty--' %]</li>
+ <li>lang_code: [% lang_code %]</li>
+ <li>user.id: [% c.user.id || '--not logged in--' %]</li>
+</ul>
+
+<style type="text/css">
+ #overrides_form {
+ font-size: 80%;
+ }
+ #overrides_form label {
+ float: left;
+ text-align: right;
+ padding-right: 0.5em;
+ width: 12em;
+ }
+
+</style>
+
+<!-- Use a post so that we don't clutter up the url -->
+<form action="" method="post" id="overrides_form" name="overrides_form">
+
+ <label for="_override_clear_all">Clear all overrides:</label>
+ <input type="checkbox" name="_override_clear_all" id="_override_clear_all" value="1"><br>
+
+ [% FOREACH k IN ['cobrand_moniker', 'lang'] %]
+ <label for="override_[% k %]">[% k %]:</label>
+ <input type="text" name="_override_[% k %]" id="override_[% k %]" value="[% c.get_override(k)%]"><br>
+ [% END %]
+
+ <label>&nbsp;</label><input type="submit" value="Change overrides">
+</form>
+
+[% END %]
+
diff --git a/templates/web/default/debug_header.html b/templates/web/default/debug_header.html
new file mode 100644
index 000000000..1468d171c
--- /dev/null
+++ b/templates/web/default/debug_header.html
@@ -0,0 +1,5 @@
+[% IF c.config.STAGING_SITE %]
+ <p class="dev-site-notice">
+ [% loc("This is a developer site; things might break at any time, and the database will be periodically deleted.") %]
+ </p>
+[% END %]
diff --git a/templates/web/default/email_sent.html b/templates/web/default/email_sent.html
new file mode 100644
index 000000000..47a6f82cf
--- /dev/null
+++ b/templates/web/default/email_sent.html
@@ -0,0 +1,34 @@
+[% INCLUDE 'header.html', title => loc('Create a report') %]
+
+[%
+ messages = {
+ problem => {
+ action => loc('your problem will not be posted'),
+ worry => loc("we'll hang on to your problem report while you're checking your email."),
+ },
+ update => {
+ action => loc('your update will not be posted'),
+ worry => loc("we'll hang on to your update while you're checking your email."),
+ },
+ alert => {
+ action => loc('your alert will not be activated'),
+ worry => loc("we'll hang on to your alert while you're checking your email."),
+ },
+ tms => {
+ action => 'your expression of interest will not be registered',
+ worry => "we'll hang on to your expression of interest while you're checking your email.",
+ }
+ }
+%]
+
+<h1>[% loc("Nearly Done! Now check your email...") %]</h1>
+
+<p>[% loc("The confirmation email <strong>may</strong> take a few minutes to arrive &mdash; <em>please</em> be patient.") %]</p>
+
+<p>[% loc("If you use web-based email or have 'junk mail' filters, you may wish to check your bulk/spam mail folders: sometimes, our messages are marked that way.") %]</p>
+
+<p>[% tprintf( loc("You must now click the link in the email we've just sent you &mdash; if you do not, %s."), messages.$email_type.action ) %].</p>
+
+<p>[% tprintf( loc("(Don't worry &mdash; %s)"), messages.$email_type.worry ) %]</p>
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/errors.html b/templates/web/default/errors.html
new file mode 100644
index 000000000..e0f484ba5
--- /dev/null
+++ b/templates/web/default/errors.html
@@ -0,0 +1,5 @@
+[% FOREACH error IN errors %]
+ [% '<ul class="error">' IF loop.first %]
+ <li>[% error %]</li>
+ [% '</ul>' IF loop.last %]
+[% END %]
diff --git a/templates/web/default/errors/page_error_404_not_found.html b/templates/web/default/errors/page_error_404_not_found.html
new file mode 100644
index 000000000..77db18e89
--- /dev/null
+++ b/templates/web/default/errors/page_error_404_not_found.html
@@ -0,0 +1,20 @@
+[% INCLUDE 'header.html', title => loc('Page Not Found') %]
+
+<h1>[%loc('Page Not Found') %]</h1>
+
+[% IF error_msg %]
+ <p class="error">[% error_msg | html %]</p>
+[% END %]
+
+<p>
+ [%
+ tprintf(
+ loc("The requested URL '%s' was not found on this server"),
+ c.req.uri
+ )
+ %]
+</p>
+
+[% # FIXME - add more helpful suggestions to this page %]
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/errors/page_error_410_gone.html b/templates/web/default/errors/page_error_410_gone.html
new file mode 100644
index 000000000..c4d5035d8
--- /dev/null
+++ b/templates/web/default/errors/page_error_410_gone.html
@@ -0,0 +1,20 @@
+[% INCLUDE 'header.html', title => loc('Page Gone') %]
+
+<h1>[%loc('Page Gone') %]</h1>
+
+[% IF error_msg %]
+ <p class="error">[% error_msg | html %]</p>
+[% END %]
+
+<p>
+ [%
+ tprintf(
+ loc("The requested URL '%s' is no longer available"),
+ c.req.uri
+ )
+ %]
+</p>
+
+[% # FIXME - add more helpful suggestions to this page %]
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/website/faq-en-gb b/templates/web/default/faq/faq-en-gb.html
index 59713ee61..2da32fc19 100755
--- a/templates/website/faq-en-gb
+++ b/templates/web/default/faq/faq-en-gb.html
@@ -1,3 +1,5 @@
+[% INCLUDE 'header.html', title => loc('Frequently Asked Questions') %]
+
<h1>Frequently Asked Questions</h1>
<dl>
<dt>What is FixMyStreet?</dt>
@@ -137,13 +139,6 @@ unless we are obliged to by law. Your name will not be published anywhere unless
problem you&rsquo;ve reported, and send you a questionnaire email four weeks
after you submit a problem, asking for a status update; we&rsquo;ll only ever
send you emails in relation to your problem.</dd>
- <dt>What's this about the Guardian?</dt>
- <dd>mySociety and the Guardian are working together to provide local versions of
-FixMyStreet in Leeds, Edinburgh and Cardiff as part of the Guardian Local project. If you submit a problem or
-provide an update in one of those cities, administrators from both mySociety and the Guardian will be able to see your
-details. They will never use them for anything other than to help administer FixMyStreet, in accordance with this privacy
-policy, and the Guardian's <a href="http://users.guardian.co.uk/help/article/0,,933905,00.html">privacy policy</a>.
- </dd>
</dl>
<h2>Organisation Questions</h2>
<dl>
@@ -207,3 +202,4 @@ our servers).
Let us know if we&rsquo;ve missed anyone.</dd>
</dl>
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/footer.html b/templates/web/default/footer.html
new file mode 100644
index 000000000..9222436e2
--- /dev/null
+++ b/templates/web/default/footer.html
@@ -0,0 +1,24 @@
+</div>
+</div>
+
+<h2 class="v">[% loc('Navigation') %]</h2>
+<ul id="navigation">
+<li><a href="/">[% loc("Report a problem") %]</a></li>
+<li><a href="/reports">[% loc("All reports") %]</a></li>
+<li><a href="/alert[% pc ? '/list?pc=' : '' %][% pc | uri %]">[% loc("Local alerts") %]</a></li>
+<li><a href="/faq">[% loc("Help") %]</a></li>
+<li><a href="/contact">[% loc("Contact") %]</a></li>
+</ul>
+
+[% loc('<a href="http://www.mysociety.org/"><img id="logo" width="133" height="26" src="/i/mysociety-dark.png" alt="View mySociety.org"><span id="logoie"></span></a>') %]
+
+<p id="footer">
+ [% loc('Built by <a href="http://www.mysociety.org/">mySociety</a>, using some <a href="http://github.com/mysociety/fixmystreet">clever</a>&nbsp;<a href="https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa">code</a>.') %]
+</p>
+
+[% INCLUDE 'tracking_code.html' %]
+
+[% INCLUDE 'debug_footer.html' %]
+
+</body>
+</html>
diff --git a/templates/web/default/front_stats.html b/templates/web/default/front_stats.html
new file mode 100644
index 000000000..4b98ef31e
--- /dev/null
+++ b/templates/web/default/front_stats.html
@@ -0,0 +1,46 @@
+[%
+ USE Comma;
+ # Note - if we want to i18n the commas we should try
+ # 'Template::Plugin::Number::Format'
+%]
+
+<h2>[% loc('FixMyStreet updates') %]</h2>
+
+[%
+ stats = c.cobrand.front_stats_data();
+
+ new_text =
+ stats.recency == '1 week'
+ ? nget(
+ "<big>%s</big> report in past week",
+ "<big>%s</big> reports in past week",
+ stats.new
+ )
+ : nget(
+ "<big>%s</big> report recently",
+ "<big>%s</big> reports recently",
+ stats.new
+ );
+
+ fixed_text = nget(
+ "<big>%s</big> fixed in past month",
+ "<big>%s</big> fixed in past month",
+ stats.fixed
+ );
+
+ updates_text = nget(
+ "<big>%s</big> update on reports",
+ "<big>%s</big> updates on reports",
+ stats.updates
+ );
+
+%]
+
+<div id="front_stats">
+ <div>[% tprintf( new_text, stats.new ) | comma %]</div>
+ [% IF c.cobrand.moniker != 'emptyhomes' %]
+ <div>[% tprintf( fixed_text, stats.fixed ) | comma %]</div>
+ [% END %]
+ <div>[% tprintf( updates_text, stats.updates ) | comma %]</div>
+</div>
+
diff --git a/templates/web/default/header.html b/templates/web/default/header.html
new file mode 100644
index 000000000..52f066663
--- /dev/null
+++ b/templates/web/default/header.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="[% lang_code %]">
+ <head>
+
+ [% INCLUDE 'common_header_tags.html' %]
+
+ <style type="text/css">@import url("/css/core.css"); @import url("/css/main.css");</style>
+ <!--[if LT IE 7]>
+ <style type="text/css">@import url("/css/ie6.css");</style>
+ <![endif]-->
+
+ </head>
+ <body>
+
+ [% IF NOT title AND NOT c.req.path %]<h1 id="header">[% ELSE %]<div id="header"><a href="/">[% END %]
+ [%- loc('Fix<span id="my">My</span>Street') %]
+ [%- IF NOT title AND NOT c.req.path %]</h1>[% ELSE %]</a></div>[% END %]
+
+ <div id="wrapper"><div id="mysociety">
+
+ [% INCLUDE 'debug_header.html' %]
diff --git a/templates/web/default/index.html b/templates/web/default/index.html
new file mode 100644
index 000000000..5dffb5f10
--- /dev/null
+++ b/templates/web/default/index.html
@@ -0,0 +1,107 @@
+[% INCLUDE 'header.html', title => '' %]
+
+<p id="expl">
+ [%
+ subhead
+ = c.cobrand.moniker == 'southampton'
+ ? '(like graffiti, fly tipping, or broken paving slabs)'
+ : loc('(like graffiti, fly tipping, broken paving slabs, or street lighting)');
+ %]
+
+ <strong>[% loc('Report, view, or discuss local problems') %]</strong>
+
+ [% IF subhead != ' ' %]
+ <br><small>[% subhead %]</small>
+ [% END %]
+</p>
+
+
+[% # --- insert iPhone details, not converted from old cgi script yet
+ #
+ # if (my $url = mySociety::Config::get('IPHONE_URL')) {
+ # my $getiphone = _("Get FixMyStreet on your iPhone");
+ # my $new = _("New!");
+ # if ($q->{site} eq 'fixmystreet') {
+ # $out .= <<EOF
+ # <p align="center" style="margin-bottom:0">
+ # <img width="23" height="12" alt="$new" src="/i/new.png" border="0">
+ # <a href="$url">$getiphone</a>
+ # </p>
+ # EOF
+ # }
+%]
+
+
+[% IF error %]
+ <p class="error">[% error %]</p>
+[% END %]
+
+
+[%
+ question
+ = c.cobrand.enter_postcode_text()
+ || loc("Enter a nearby GB postcode, or street name and area");
+%]
+
+<form action="[% c.uri_for('/around') %]" method="get" name="postcodeForm" id="postcodeForm">
+ <label for="pc">[% question %]:</label>
+ &nbsp;<input type="text" name="pc" value="" id="pc" size="10" maxlength="200">
+ &nbsp;<input type="submit" value="[% loc('Go') %]" id="submit">
+ [% c.cobrand.form_elements('postcodeForm') %]
+</form>
+
+
+<div id="front_intro">
+
+ <h2>[% loc('How to report a problem') %]</h2>
+
+ <ol>
+ <li>[% question %]</li>
+ <li>[% loc('Locate the problem on a map of the area') %]</li>
+ <li>[% loc('Enter details of the problem') %]</li>
+ <li>[% loc('We send it to the council on your behalf') %]</li>
+ </ol>
+
+ [% INCLUDE "front_stats.html" %]
+
+</div>
+
+[%
+ recent_photos = c.cobrand.recent_photos(3);
+ probs = c.cobrand.recent();
+%]
+
+
+[% IF probs.size || recent_photos.size %]
+<div id="front_recent">
+ [% IF recent_photos.size %]
+ <h2>[% loc('Photos of recent reports') %]</h2>
+ [% FOREACH p IN recent_photos %]
+ <a href="/report/[% p.id %]"><img border="0" height="100"
+ src="/photo?tn=1;id=[% p.id %]" alt="[% p.title | html %]" title="[% p.title | html %]"></a>
+ [% END %]
+ [% END %]
+
+ [% IF probs.size %]
+
+ <h2>[% loc('Recently reported problems') %]</h2>
+
+ <ul>
+ [% FOREACH p IN probs %]
+ <li>
+ <a href="/report/[% p.id %]">[% p.title | html %]</a>
+ </li>
+ [% END %]
+ </ul>
+ [% END %]
+</div>
+[% END %]
+
+[% IF c.cobrand.moniker == 'emptyhomes' %]
+<div id="eha_advert">
+ Now is the best time to turn empty properties into empty homes... Don't miss it!
+ <a href="http://www.emptyhomes.com/EHConference2011.html">Home Again: Empty Homes National Conference 2011</a>
+</div>
+[% END %]
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/maps/bing.html b/templates/web/default/maps/bing.html
new file mode 100644
index 000000000..6b7114199
--- /dev/null
+++ b/templates/web/default/maps/bing.html
@@ -0,0 +1,18 @@
+[% map_js = BLOCK %]
+<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&mkt=en-GB"></script>
+<script type="text/javascript" src="/js/map-bing.js"></script>
+[% END %]
+
+[% map_html = BLOCK %]
+<script type="text/javascript">
+var fixmystreet = {
+ 'key': '[% map.key %]',
+ 'latitude': [% map.latitude %],
+ 'longitude': [% map.longitude %],
+ 'pins': [% INCLUDE maps/pins_js.html %]
+}
+</script>
+<div id="map_box">
+ [% pre_map %]
+ <div id="map"></div>
+[% END %]
diff --git a/templates/web/default/maps/fms.html b/templates/web/default/maps/fms.html
new file mode 100644
index 000000000..36fd48467
--- /dev/null
+++ b/templates/web/default/maps/fms.html
@@ -0,0 +1,11 @@
+[% map_js = BLOCK %]
+<!-- <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&mkt=en-GB"></script> -->
+<script type="text/javascript" src="/jslib/OpenLayers-2.10/OpenLayers.js"></script>
+<script type="text/javascript" src="/js/map-OpenLayers.js"></script>
+<script type="text/javascript" src="/js/map-bing-ol.js"></script>
+[% END %]
+
+[% map_html = BLOCK %]
+[% INCLUDE maps/openlayers.html %]
+[% END %]
+
diff --git a/templates/web/default/maps/google.html b/templates/web/default/maps/google.html
new file mode 100644
index 000000000..2e326734b
--- /dev/null
+++ b/templates/web/default/maps/google.html
@@ -0,0 +1,17 @@
+[% map_js = BLOCK %]
+<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
+<script type="text/javascript" src="/js/map-google.js"></script>
+[% END %]
+
+[% map_html = BLOCK %]
+<script type="text/javascript">
+var fixmystreet = {
+ 'latitude': [% map.latitude %],
+ 'longitude': [% map.longitude %],
+ 'pins': [% INCLUDE maps/pins_js.html %]
+}
+</script>
+<div id="map_box">
+ [% pre_map %]
+ <div id="map"></div>
+[% END %]
diff --git a/templates/web/default/maps/openlayers.html b/templates/web/default/maps/openlayers.html
new file mode 100644
index 000000000..ae670ce13
--- /dev/null
+++ b/templates/web/default/maps/openlayers.html
@@ -0,0 +1,86 @@
+[% IF map.clickable %]
+ [% map.img_type = 'input type="image"' %]
+[% ELSE %]
+ [% map.img_type = 'img' %]
+[% END %]
+
+<input type="hidden" name="zoom" value="[% map.zoom %]">
+<script type="text/javascript">
+var fixmystreet = {
+ 'latitude': [% map.latitude %],
+ 'longitude': [% map.longitude %],
+[% IF map.zoom -%]
+ 'zoom': [% map.zoom %],
+[%- END %]
+ 'map_type': [% map.map_type %],
+ 'pins': [% INCLUDE maps/pins_js.html %]
+}
+</script>
+<div id="map_box">
+ [% pre_map %]
+ <div id="map"><noscript>
+ <div id="drag"><[% map.img_type %]
+ alt="NW map tile" id="t2.2"
+ name="tile_[% map.x_tile - 1 %].[% map.y_tile - 1 %]"
+ src="[% map.tiles.0 %]"
+ style="top:0; left:0;"><[% map.img_type %]
+ alt="NE map tile" id="t2.3"
+ name="tile_[% map.x_tile %].[% map.y_tile - 1 %]"
+ src="[% map.tiles.1 %]"
+ style="top:0px; left:256px;"><br><[% map.img_type %]
+ alt="SW map tile" id="t3.2"
+ name="tile_[% map.x_tile - 1 %].[% map.y_tile %]"
+ src="[% map.tiles.2 %]"
+ style="top:256px; left:0;"><[% map.img_type %]
+ alt="SE map tile" id="t3.3"
+ name="tile_[% map.x_tile %].[% map.y_tile %]"
+ src="[% map.tiles.3 %]"
+ style="top:256px; left:256px;"></div>
+ <div id="pins">[% FOR pin IN map.pins %][% INCLUDE pin %][% END %]</div>
+ [% INCLUDE compass %]
+ </noscript></div>
+ <p id="copyright">
+ [% map.copyright %]
+ </p>
+
+
+[% BLOCK compass %]
+[%
+ north = c.uri_with( { lat = map.compass.north.0, lon = map.compass.north.1, zoom = map.zoom } )
+ south = c.uri_with( { lat = map.compass.south.0, lon = map.compass.south.1, zoom = map.zoom } )
+ east = c.uri_with( { lat = map.compass.east.0, lon = map.compass.east.1, zoom = map.zoom } )
+ west = c.uri_with( { lat = map.compass.west.0, lon = map.compass.west.1, zoom = map.zoom } )
+ world = c.uri_with( { zoom = 0 } );
+ SET zoom_in = c.uri_with( { zoom = map.zoom + 1 } ) IF map.zoom < map.zoom_levels - 1;
+ SET zoom_out = c.uri_with( { zoom = map.zoom - 1 } ) IF map.zoom > 0;
+ SET zoom_in = '#' IF map.zoom >= map.zoom_levels - 1;
+ SET zoom_out = '#' IF map.zoom <= 0;
+%]
+<div style="position: absolute; left: 4px; top: 4px; z-index: 1007;" class="olControlPanZoom olControlNoSelect" unselectable="on">
+ <div style="position: absolute; left: 13px; top: 4px; width: 18px; height: 18px;"><a href="[% north %]"><img style="position: relative; width: 18px; height: 18px;" src="/jslib/OpenLayers-2.10/img/north-mini.png" border="0"></a></div>
+ <div style="position: absolute; left: 4px; top: 22px; width: 18px; height: 18px;"><a href="[% west %]"><img style="position: relative; width: 18px; height: 18px;" src="/jslib/OpenLayers-2.10/img/west-mini.png" border="0"></a></div>
+ <div style="position: absolute; left: 22px; top: 22px; width: 18px; height: 18px;"><a href="[% east %]"><img style="position: relative; width: 18px; height: 18px;" src="/jslib/OpenLayers-2.10/img/east-mini.png" border="0"></a></div>
+ <div style="position: absolute; left: 13px; top: 40px; width: 18px; height: 18px;"><a href="[% south %]"><img style="position: relative; width: 18px; height: 18px;" src="/jslib/OpenLayers-2.10/img/south-mini.png" border="0"></a></div>
+ <div style="position: absolute; left: 13px; top: 63px; width: 18px; height: 18px;"><a href="[% zoom_in %]"><img style="position: relative; width: 18px; height: 18px;" src="/jslib/OpenLayers-2.10/img/zoom-plus-mini.png" border="0"></a></div>
+ <div style="position: absolute; left: 13px; top: 81px; width: 18px; height: 18px;"><a href="[% world %]"><img style="position: relative; width: 18px; height: 18px;" src="/jslib/OpenLayers-2.10/img/zoom-world-mini.png" border="0"></a></div>
+ <div style="position: absolute; left: 13px; top: 99px; width: 18px; height: 18px;"><a href="[% zoom_out %]"><img style="position: relative; width: 18px; height: 18px;" src="/jslib/OpenLayers-2.10/img/zoom-minus-mini.png" border="0"></a></div>
+</div>
+[% END %]
+
+
+[% BLOCK pin %]
+
+[% cols = {
+ red = 'R', green = 'G', blue = 'B', purple = 'P',
+ }
+%]
+[% IF pin.id %]
+<a title="[% pin.title | html %]" href="[% c.uri_for('/report/' _ pin.id) %]">
+[%- END -%]
+<img border="0" class="pin" src="[% c.uri_for('/i/pin' _ cols.${pin.colour} _ '.gif') %]"
+ alt="[% loc('Problem') %]" style="top:[% pin.py - 59 %]px; left:[% pin.px %]px; position: absolute;">
+[%- IF pin.id -%]
+</a>
+[% END %]
+
+[% END %]
diff --git a/templates/web/default/maps/osm-streetview.html b/templates/web/default/maps/osm-streetview.html
new file mode 100644
index 000000000..ba8d1fba4
--- /dev/null
+++ b/templates/web/default/maps/osm-streetview.html
@@ -0,0 +1,9 @@
+[% map_js = BLOCK %]
+<script type="text/javascript" src="/jslib/OpenLayers-2.10/OpenLayers.js"></script>
+<script type="text/javascript" src="/js/map-OpenLayers.js"></script>
+<script type="text/javascript" src="/js/map-streetview.js"></script>
+[% END %]
+
+[% map_html = BLOCK %]
+[% INCLUDE maps/openlayers.html %]
+[% END %]
diff --git a/templates/web/default/maps/osm.html b/templates/web/default/maps/osm.html
new file mode 100644
index 000000000..c6529566e
--- /dev/null
+++ b/templates/web/default/maps/osm.html
@@ -0,0 +1,9 @@
+[% map_js = BLOCK %]
+<script type="text/javascript" src="/jslib/OpenLayers-2.10/OpenLayers.js"></script>
+<script type="text/javascript" src="/js/map-OpenLayers.js"></script>
+<script type="text/javascript" src="/js/map-OpenStreetMap.js"></script>
+[% END %]
+
+[% map_html = BLOCK %]
+[% INCLUDE maps/openlayers.html %]
+[% END %]
diff --git a/templates/web/default/maps/pins_js.html b/templates/web/default/maps/pins_js.html
new file mode 100644
index 000000000..4a5814b15
--- /dev/null
+++ b/templates/web/default/maps/pins_js.html
@@ -0,0 +1,4 @@
+[ [% FOR pin IN map.pins -%]
+ [ [% pin.latitude %], [% pin.longitude %], '[% pin.colour %]', '[% pin.id %]', '[% pin.title %]' ]
+ [%- IF !loop.last %],[% END %]
+[% END %] ]
diff --git a/templates/web/default/maps/tilma/openlayers.html b/templates/web/default/maps/tilma/openlayers.html
new file mode 100644
index 000000000..c8ae53bfe
--- /dev/null
+++ b/templates/web/default/maps/tilma/openlayers.html
@@ -0,0 +1,26 @@
+[% map_js = BLOCK %]
+<script type="text/javascript" src="/jslib/OpenLayers-2.10/OpenLayers.js"></script>
+<script type="text/javascript" src="/js/map-OpenLayers.js"></script>
+<script type="text/javascript" src="/js/map-tilma-ol.js"></script>
+<script type="text/javascript" src="/js/OpenLayers.Projection.OrdnanceSurvey.js"></script>
+[% END %]
+
+[% map_html = BLOCK %]
+<script type="text/javascript">
+var fixmystreet = {
+ 'tilewidth': [% map.tilewidth %],
+ 'tileheight': [% map.tilewidth %],
+ 'latitude': [% map.latitude %],
+ 'longitude': [% map.longitude %],
+ 'pins': [% INCLUDE maps/pins_js.html %],
+ 'tile_type': '[% map.tile_type %]',
+ 'maxResolution': [% map.maxResolution %]
+};
+</script>
+<div id="map_box">
+ [% pre_map %]
+ <div id="map">
+ [% '<div id="watermark"></div>' IF map.watermark %]
+ </div>
+ <p id="copyright">[% map.copyright %]</p>
+[% END %]
diff --git a/templates/web/default/maps/tilma/original.html b/templates/web/default/maps/tilma/original.html
new file mode 100644
index 000000000..e02a974f8
--- /dev/null
+++ b/templates/web/default/maps/tilma/original.html
@@ -0,0 +1,88 @@
+[% map_js = BLOCK %]
+<script type="text/javascript" src="/js/map-tilma.js"></script>
+[% END %]
+
+[% map_html = BLOCK %]
+
+[% IF !map.tiles %]
+ <div id="map_box">
+ <div id="map"><div id="drag">
+ [% loc("Unable to fetch the map tiles from the tile server.") %]
+ </div></div>
+ [% RETURN %]
+[% END %]
+
+[% IF map.clickable %]
+<input type="hidden" name="x" id="formX" value="[% map.x %]">
+<input type="hidden" name="y" id="formY" value="[% map.y %]">
+ [% map.img_type = 'input type="image"' %]
+[% ELSE %]
+ [% map.img_type = 'img' %]
+[% END %]
+<script type="text/javascript">
+[% c.cobrand.root_path_js %]
+var fixmystreet = {
+ 'x': [% map.x - 3 %],
+ 'y': [% map.y - 3 %],
+ 'start_x': [% map.px %],
+ 'start_y': [% map.py %],
+ 'tile_type': '[% map.tile_type %]',
+ 'tilewidth': [% map.tilewidth %],
+ 'tileheight': [% map.tilewidth %]
+};
+</script>
+<div id="map_box">
+ [% pre_map %]
+ <div id="map"><div id="drag">
+ <[% map.img_type %]
+ alt="NW map tile" id="t2.2" name="tile_[% map.x - 1 %].[% map.y %]" src="[% map.url %][% map.tileids.0.0 %]" style="top:0px; left:0;"><[% map.img_type %]
+ alt="NE map tile" id="t2.3" name="tile_[% map.x %].[% map.y %]" src="[% map.url %][% map.tileids.0.1 %]" style="top:0px; left:[% map.tilewidth %]px;"><br><[% map.img_type %]
+ alt="SW map tile" id="t3.2" name="tile_[% map.x - 1 %].[% map.y - 1 %]" src="[% map.url %][% map.tileids.1.0 %]" style="top:[% map.tilewidth %]px; left:0;"><[% map.img_type %]
+ alt="SE map tile" id="t3.3" name="tile_[% map.x %].[% map.y - 1 %]" src="[% map.url %][% map.tileids.1.1 %]" style="top:[% map.tilewidth %]px; left:[% map.tilewidth %]px;">
+ <div id="pins">[% FOR pin IN map.pins %][% INCLUDE pin %][% END %]</div>
+ </div>
+ [% '<div id="watermark"></div>' IF map.watermark %]
+ [% INCLUDE compass %]
+ </div>
+ <p id="copyright">[% map.copyright %]</p>
+[% END %]
+
+
+[% BLOCK pin %]
+
+[% num = '' IF !num or num > 9;
+ cols = {
+ red = 'R', green = 'G', blue = 'B', purple = 'P',
+ }
+%]
+[% IF pin.id %]
+<a title="[% pin.title | html %]" href="[% c.uri_for('/report/' _ pin.id) %]">
+[%- END -%]
+<img class="pin" src="[% c.uri_for('/i/pin' _ cols.${pin.colour} _ num _ '.gif') %]"
+ alt="[% loc('Problem') %]" style="top:[% pin.py - 59 %]px; left:[% pin.px %]px; position: absolute;">
+[%- IF pin.id -%]
+</a>
+[% END %]
+
+[% END %]
+
+
+[% BLOCK compass %]
+ <table cellpadding="0" cellspacing="0" border="0" id="compass">
+ <tr valign="bottom">
+ <td align="right"><a href="[% c.req.uri_with( { x => map.x - 1, y => map.y + 1 } ) %]"><img src="[% c.uri_for('/i/arrow-northwest.gif') %]" alt="NW" width=11 height=11></a></td>
+ <td align="center"><a href="[% c.req.uri_with( { x => map.x, y => map.y + 1 } ) %]"><img src="[% c.uri_for('/i/arrow-north.gif') %]" vspace="3" alt="N" width=13 height=11></a></td>
+ <td><a href="[% c.req.uri_with( { x => map.x + 1, y => map.y + 1 } ) %]"><img src="[% c.uri_for('/i/arrow-northeast.gif') %]" alt="NE" width=11 height=11></a></td>
+ </tr>
+ <tr>
+ <td><a href="[% c.req.uri_with( { x => map.x - 1, y => map.y } ) %]"><img src="[% c.uri_for('/i/arrow-west.gif') %]" hspace="3" alt="W" width=11 height=13></a></td>
+ <td align="center"><a href="$recentre"><img src="[% c.uri_for('/i/rose.gif') %]" alt="Recentre" width=35 height=34></a></td>
+ <td><a href="[% c.req.uri_with( { x => map.x + 1, y => map.y } ) %]"><img src="[% c.uri_for('/i/arrow-east.gif') %]" hspace="3" alt="E" width=11 height=13></a></td>
+ </tr>
+ <tr valign="top">
+ <td align="right"><a href="[% c.req.uri_with( { x => map.x - 1, y => map.y - 1 } ) %]"><img src="[% c.uri_for('/i/arrow-southwest.gif') %]" alt="SW" width=11 height=11></a></td>
+ <td align="center"><a href="[% c.req.uri_with( { x => map.x, y => map.y - 1 } ) %]"><img src="[% c.uri_for('/i/arrow-south.gif') %]" vspace="3" alt="S" width=13 height=11></a></td>
+ <td><a href="[% c.req.uri_with( { x => map.x + 1, y => map.y - 1 } ) %]"><img src="[% c.uri_for('/i/arrow-southeast.gif') %]" alt="SE" width=11 height=11></a></td>
+ </tr>
+ </table>
+[% END %]
diff --git a/templates/web/default/my/my.html b/templates/web/default/my/my.html
new file mode 100644
index 000000000..03b180a3a
--- /dev/null
+++ b/templates/web/default/my/my.html
@@ -0,0 +1,18 @@
+[% INCLUDE 'header.html', title => loc('My Reports') %]
+
+<h1>[% loc('Your Reports') %]</h1>
+
+[% FOREACH p = c.user.problems %]
+ [% "<ul>" IF loop.first %]
+
+ <li><a href="[% c.uri_for( '/report', p.id ) %]">[% p.title | html %]</a> ([% loc(p.state) %])</li>
+
+ [% "</ul>" IF loop.last %]
+[% END %]
+
+
+[%# FIXME - put in blurb here %]
+
+<a href="/auth/change_password">change password</a>
+
+[% INCLUDE 'footer.html' %] \ No newline at end of file
diff --git a/templates/web/default/questionnaire/completed.html b/templates/web/default/questionnaire/completed.html
new file mode 100644
index 000000000..4a6419ccb
--- /dev/null
+++ b/templates/web/default/questionnaire/completed.html
@@ -0,0 +1,28 @@
+[% advert_outcome = 1 %]
+
+[% IF been_fixed == 'Unknown' %]
+
+[% loc('<p>Thank you very much for filling in our questionnaire; if you
+get some more information about the status of your problem, please come back to the
+site and leave an update.</p>') %]
+
+[% ELSIF new_state == 'confirmed' OR (!new_state AND problem.state == 'confirmed') %]
+
+[% tprintf( loc('<p style="font-size:150%%">We&rsquo;re sorry to hear that. We have two suggestions: why not try
+<a href="%s">writing direct to your councillor(s)</a>
+or, if it&rsquo;s a problem that could be fixed by local people working together,
+why not <a href="http://www.pledgebank.com/new">make and publicise a pledge</a>?
+</p>'), c.cobrand.writetothem_url || 'http://www.writetothem.com/' ) %]
+
+[% advert_outcome = 0 %]
+
+[% ELSE %]
+
+[% loc('<p style="font-size:150%">Thank you very much for filling in our questionnaire; glad to hear it&rsquo;s been fixed.</p>') %]
+
+[% END %]
+
+[% IF advert_outcome %]
+ [% display_crosssell_advert( problem.user.email, problem.name, 'council', problem.council ) %]
+[% END %]
+
diff --git a/templates/web/default/questionnaire/creator_fixed.html b/templates/web/default/questionnaire/creator_fixed.html
new file mode 100644
index 000000000..0d3181ec1
--- /dev/null
+++ b/templates/web/default/questionnaire/creator_fixed.html
@@ -0,0 +1,23 @@
+[% INCLUDE 'header.html', title = loc('Confirmation') %]
+
+<form method="post" action="/questionnaire/submit" id="questionnaire">
+<input type="hidden" name="problem" value="[% problem_id | html %]">
+
+[% INCLUDE 'errors.html' %]
+
+<p>
+[% loc("Thanks, glad to hear it's been fixed! Could we just ask if you have ever reported a problem to a council before?") %]
+</p>
+
+<p align="center">
+<input type="radio" name="reported" id="reported_yes" value="Yes"[% ' checked' IF reported == 'Yes' %]>
+<label for="reported_yes">[% loc('Yes') %]</label>
+<input type="radio" name="reported" id="reported_no" value="No"[% ' checked' IF reported == 'No' %]>
+<label for="reported_no">[% loc('No') %]</label>
+</p>
+
+<p><input type="submit" name="submit" value="[% loc('Submit') %]"></p>
+
+</form>
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/questionnaire/error.html b/templates/web/default/questionnaire/error.html
new file mode 100755
index 000000000..12aa8e170
--- /dev/null
+++ b/templates/web/default/questionnaire/error.html
@@ -0,0 +1,8 @@
+[% INCLUDE 'header.html', title = loc('Error') %]
+
+<h1>[% loc('Error') %]</h1>
+
+<p>[% message %]</p>
+
+[% INCLUDE 'footer.html' %]
+
diff --git a/templates/web/default/questionnaire/index.html b/templates/web/default/questionnaire/index.html
new file mode 100644
index 000000000..d463ff2f9
--- /dev/null
+++ b/templates/web/default/questionnaire/index.html
@@ -0,0 +1,121 @@
+[%
+ pre_map = INCLUDE 'report/_main.html';
+ PROCESS "maps/${map.type}.html";
+ INCLUDE 'header.html', title = loc('Questionnaire')
+%]
+
+[% map_html %]
+
+[% INCLUDE 'report/updates.html' %]
+
+</div>
+<div id="side">
+
+<h1>[% loc('Questionnaire') %]</h1>
+
+<form method="post" action="/questionnaire/submit" id="questionnaire"
+[%- IF c.cobrand.allow_photo_upload -%]
+ enctype="multipart/form-data"
+[%- END -%]
+>
+
+<input type="hidden" name="token" value="[% token | html %]">
+
+[% c.cobrand.form_elements('questionnaireForm') %]
+
+[% IF c.cobrand.moniker == 'emptyhomes' %]
+[% IF num_questionnaire == 1 %]
+[% loc('<p>Getting empty homes back into use can be difficult. You shouldn&rsquo;t expect
+the property to be back into use yet. But a good council will have started work
+and should have reported what they have done on the website. If you are not
+satisfied with progress or information from the council, now is the right time
+to say. You may also want to try contacting some other people who may be able
+to help. For advice on how to do this and other useful information please
+go to <a href="http://www.emptyhomes.com/getinvolved/campaign.html">http://www.emptyhomes.com/getinvolved/campaign.html</a>.</p>
+') %]
+[% ELSE %]
+[% loc('<p>Getting empty homes back into use can be difficult, but by now a good council
+will have made a lot of progress and reported what they have done on the
+website. Even so properties can remain empty for many months if the owner is
+unwilling or the property is in very poor repair. If nothing has happened or
+you are not satisfied with the progress the council is making, now is the right
+time to say so. We think it&rsquo;s a good idea to contact some other people who
+may be able to help or put pressure on the council For advice on how to do
+this and other useful information please go to <a
+href="http://www.emptyhomes.com/getinvolved/campaign.html">http://www.emptyhomes.com/getinvolved/campaign.html</a>.</p>
+') %]
+[% END %]
+[% END %]
+
+<p>
+[% loc('The details of your problem are available on the right hand side of this page.') %]
+[% loc('Please take a look at the updates that have been left.') IF updates %]
+</p>
+
+[% IF errors %]
+<ul class="error">
+<li>[% errors.join("</li>\n<li>") %]</li>
+</ul>
+[% END %]
+
+<p>
+[% loc('An update marked this problem as fixed.') IF problem.state == 'fixed' %]
+[% loc('Has this problem been fixed?') %]
+</p>
+
+<p>
+<input type="radio" name="been_fixed" id="been_fixed_yes" value="Yes"[% ' checked' IF been_fixed == 'Yes' %]>
+<label for="been_fixed_yes">[% loc('Yes') %]</label>
+<input type="radio" name="been_fixed" id="been_fixed_no" value="No"[% ' checked' IF been_fixed == 'No' %]>
+<label for="been_fixed_no">[% loc('No') %]</label>
+<input type="radio" name="been_fixed" id="been_fixed_unknown" value="Unknown"[% ' checked' IF been_fixed == 'Unknown' %]>
+<label for="been_fixed_unknown">[% loc('Don&rsquo;t know') %]</label>
+</p>
+
+[% UNLESS answered_ever_reported %]
+<p>[% loc('Have you ever reported a problem to a council before, or is this your first time?') %]</p>
+<p>
+<input type="radio" name="reported" id="reported_yes" value="Yes"[% ' checked' IF reported == 'Yes' %]>
+<label for="reported_yes">[% loc('Reported before') %]</label>
+<input type="radio" name="reported" id="reported_no" value="No"[% ' checked' IF reported == 'No' %]>
+<label for="reported_no">[% loc('First time') %]</label>
+</p>
+[% END %]
+
+<p>[% loc('If you wish to leave a public update on the problem, please enter it here
+(please note it will not be sent to the council). For example, what was
+your experience of getting the problem fixed?') %]</p>
+
+<p><textarea name="update" style="max-width:90%" rows="7" cols="30">[% update | html %]</textarea></p>
+
+[% IF c.cobrand.allow_photo_upload %]
+<div id="fileupload_normalUI">
+ [% IF upload_fileid %]
+ <p>[% loc('You have already attached a photo to this report, attaching another one will replace it.') %]</p>
+ <input type="hidden" name="upload_fileid" value="[% upload_fileid %]">
+ [% END %]
+ <label for="form_photo">[% loc('Photo:') %]</label>
+ <input type="file" name="photo" id="form_photo">
+</div>
+[% END %]
+
+[% IF c.cobrand.moniker != 'emptyhomes' %]
+<div id="another_qn">
+ <p>[% loc('Would you like to receive another questionnaire in 4 weeks, reminding you to check the status?') %]</p>
+ <p>
+ <input type="radio" name="another" id="another_yes" value="Yes"[% ' checked' IF another == 'Yes' %]>
+ <label for="another_yes">[% loc('Yes') %]</label>
+ <input type="radio" name="another" id="another_no" value="No"[% ' checked' IF another == 'No' %]>
+ <label for="another_no">[% loc('No') %]</label>
+ </p>
+</div>
+[% END %]
+
+<p><input type="submit" name="submit" value="[% loc('Submit questionnaire') %]"></p>
+
+</form>
+
+</div>
+
+[% INCLUDE 'footer.html' %]
+
diff --git a/templates/web/default/report/_main.html b/templates/web/default/report/_main.html
new file mode 100644
index 000000000..0c0c74b16
--- /dev/null
+++ b/templates/web/default/report/_main.html
@@ -0,0 +1,18 @@
+<h1>[% problem.title | html %]</h1>
+
+<p><em>[% problem.meta_line(c) | html %]
+[% IF problem.council %]
+ [% IF problem.whensent %]
+ <small class="council_sent_info"><br>[% problem.duration_string %]</small>
+ [% END %]
+[% ELSE %]
+<br><small>[% loc('Not reported to council') %]</small>
+[% END %]
+
+</em></p>
+
+[% add_links( problem.detail ) | html_para %]
+
+[% INCLUDE 'report/photo.html' object=problem center=1 %]
+
+
diff --git a/templates/web/default/report/display.html b/templates/web/default/report/display.html
new file mode 100644
index 000000000..4267b359e
--- /dev/null
+++ b/templates/web/default/report/display.html
@@ -0,0 +1,150 @@
+[%
+ PROCESS "maps/${map.type}.html";
+
+ problem_title = problem.title _ ' - ' _ loc('Viewing a problem') | html;
+ INCLUDE 'header.html'
+ title = problem_title
+ rss = [ loc('Updates to this problem, FixMyStreet'), "/rss/$problem.id" ]
+ robots = 'index, nofollow'
+%]
+
+[% map_html %]
+
+[% IF c.config.COUNTRY == 'GB' %]
+<p id='sub_map_links'>
+ <a href="http://maps.google.co.uk/maps?output=embed&amp;z=16&amp;q=
+ [%- problem.title _ ' - ' _ c.cobrand.base_url_for_emails _ '/report/' _ problem.id | uri -%]
+@[% short_latitude %],[% short_longitude %]">View on Google Maps</a></p>
+[% END %]
+
+</div>
+<div id="side">
+
+[% IF banner %]
+<p id="[% banner.id %]">
+ [% banner.text %]
+</p>
+[% END %]
+
+[% INCLUDE 'report/_main.html' %]
+
+<p align="right">
+ <small>
+ <a rel="nofollow" href="[% c.uri_for( '/contact', { id => problem.id } ) %]">[% loc('Offensive? Unsuitable? Tell us' ) %]</a>
+ </small>
+</p>
+
+<p style="padding-bottom: 0.5em; border-bottom: dotted 1px #999999;" align="right">
+ <a href="[% c.uri_for( '/around', { lat => short_latitude, lon => short_longitude } ) %]">[% loc( 'More problems nearby' ) %]</a>
+</p>
+
+<div id="alert_links">
+ <a rel="nofollow" id="email_alert" href="[% c.uri_for( '/alert/subscribe', { id => problem.id } ) %]">[% loc('Email me updates' ) %]</a>
+
+ <form action="[% c.uri_for( '/alert/subscribe' ) %]" method="post" id="email_alert_box">
+ <p>[% loc('Receive email when updates are left on this problem.' ) %]</p>
+ <label class="n" for="alert_rznvy">[% loc('Email:') %]</label>
+ <input type="text" name="rznvy" id="alert_rznvy" value="[% email | html %]" size="30">
+ <input type="hidden" name="id" value="[% problem.id %]">
+ <input type="hidden" name="type" value="updates">
+ <input type="submit" value="[% loc('Subscribe') %]">
+ [% cobrand_alert_fields %]
+ </form>
+ &nbsp;
+ <a href="[% c.uri_for( '/rss', problem.id ) %]">
+ <img src="/i/feed.png" width="16" height="16" title="[% loc('RSS feed') %]" alt="[% loc('RSS feed of updates to this problem' ) %]" border="0" style="vertical-align: middle">
+ </a>
+</div>
+
+[% INCLUDE 'report/updates.html' %]
+
+<div id="update_form">
+
+ <h2>
+ [% loc( 'Provide an update') %]
+ </h2>
+
+ [% IF c.cobrand.moniker != 'emptyhomes' %]
+ <p>
+ <small>[% loc( 'Please note that updates are not sent to the council. If you leave your name it will be public. Your information will only be used in accordance with our <a href="/faq#privacy">privacy policy</a>' ) %]</small>
+ </p>
+ [% END %]
+
+ [% INCLUDE 'errors.html' %]
+
+ <form method="post" action="[% c.uri_for( '/report/update' ) %]" name="updateForm" id="fieldset"[% IF c.cobrand.allow_photo_upload %] enctype="multipart/form-data"[% END %]>
+
+ <input type="hidden" name="submit_update" value="1">
+ <input type="hidden" name="id" value="[% problem.id | html %]">
+
+ <div>
+ <label for="form_name">[% loc( 'Name:') %]</label>
+ <input type="text" name="name" id="form_name" value="[% form_name | html %]" size="20"> [% loc( '(optional)' ) %]
+ </div>
+
+ <div class="checkbox">
+ <input type="checkbox" name="may_show_name" id="form_may_show_name" value="1"[% may_show_name %]>
+ <label for="form_may_show_name">[% loc('Can we show your name publicly?') %]</label>
+ <small>[% loc('(we never show your email address or phone number)') %]</small>
+ </div>
+
+ [% IF field_errors.email %]
+ <div class='form-error'>[% field_errors.email %]</div>
+ [% END %]
+ <div class="form-field">
+ <label for="form_rznvy">[% loc('Email' ) %]</label>
+ <input type="text" name="rznvy" id="form_rznvy" value="[% email | html %]" size="20">
+ </div>
+
+ [% IF field_errors.update %]
+ <div class='form-error'>[% field_errors.update %]</div>
+ [% END %]
+ <div class="form-field">
+ <label for="form_update">[% loc( 'Update:' ) %]</label>
+ <textarea name="update" id="form_update" rows="7" cols="30">[% update_text | html %]</textarea>
+ </div>
+
+
+ [% IF problem.state != 'fixed' %]
+ <div class="checkbox">
+ <input type="checkbox" name="fixed" id="form_fixed" value="1"[% fixed %]>
+ <label for="form_fixed">[% loc('This problem has been fixed') %]</label>
+ </div>
+ [% END %]
+
+
+ [% IF c.cobrand.allow_photo_upload %]
+ [% IF photo_error %]
+ <div class='form-error'>[% photo_error %]</div>
+ [% END %]
+ <div id="fileupload_normalUI">
+ [% IF upload_fileid %]
+ <p>[% loc('You have already attached a photo to this report, attaching another one will replace it.') %]</p>
+ <input type="hidden" name="upload_fileid" value="[% upload_fileid %]">
+ [% END %]
+ <label for="form_photo">[% loc('Photo:') %]</label>
+ <input type="file" name="photo" id="form_photo">
+ </div>
+ [% END %]
+
+
+ <div class="checkbox">
+ <input type="checkbox" name="add_alert" id="form_add_alert" value="1"[% add_alert_checked %]>
+ <label for="form_add_alert">[% loc( 'Alert me to future updates' ) %]</label>
+ </div>
+
+
+ <div class="checkbox">
+ <input type="submit" id="update_post" value="[% loc('Post') %]">
+ </div>
+
+
+ [% cobrand_update_fields %]
+
+
+ </form>
+</div>
+
+</div>
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/report/new/all_councils_text.html b/templates/web/default/report/new/all_councils_text.html
new file mode 100644
index 000000000..8514e0b0a
--- /dev/null
+++ b/templates/web/default/report/new/all_councils_text.html
@@ -0,0 +1,19 @@
+<p>
+[% IF all_councils.${area_ids_to_list.0}.type == 'LBO' %]
+[%
+ tprintf(
+ loc('All the information you provide here will be sent to <strong>%s</strong> or a relevant local body such as <strong>TfL</strong>, via the London Report-It system.'),
+ all_council_names.join( '</strong>' _ loc(' or ') _ '<strong>' )
+ );
+%]
+[% ELSE %]
+[%
+ tprintf(
+ loc('All the information you provide here will be sent to <strong>%s</strong>.'),
+ all_council_names.join( '</strong>' _ loc(' or ') _ '<strong>' )
+ );
+%]
+[% END %]
+
+[% loc('The subject and details of the problem will be public, plus your name if you give us permission.') %]
+</p>
diff --git a/templates/web/default/report/new/fill_in_details.html b/templates/web/default/report/new/fill_in_details.html
new file mode 100644
index 000000000..26f092c8e
--- /dev/null
+++ b/templates/web/default/report/new/fill_in_details.html
@@ -0,0 +1,180 @@
+[%
+ PROCESS "maps/${map.type}.html" IF report.used_map;
+ INCLUDE 'header.html', title => loc('Reporting a problem')
+%]
+
+[% IF report.used_map %]
+<form action="[% c.uri_for('/report/new') %]" method="post" name="mapForm" id="mapForm"[% IF c.cobrand.allow_photo_upload %] enctype="multipart/form-data"[% END %]>
+<input type="hidden" name="submit_map" value="1">
+<input type="hidden" name="map" value="[% c.req.params.map | html %]">
+<input type="hidden" name="pc" value="[% pc | html %]">
+[% c.cobrand.form_elements('mapForm') %]
+[% ELSE %]
+<form action="[% c.uri_for('/report/new') %]" method="post" name="mapSkippedForm"[% IF c.cobrand.allow_photo_upload %] enctype="multipart/form-data"[% END %]>
+<input type="hidden" name="pc" value="[% pc | html %]">
+<input type="hidden" name="skipped" value="1">
+[% c.cobrand.form_elements('mapSkippedForm') %]
+[% END %]
+
+<input type="hidden" name="latitude" id="fixmystreet.latitude" value="[% short_latitude | html %]">
+<input type="hidden" name="longitude" id="fixmystreet.longitude" value="[% short_longitude | html %]">
+
+[% IF report.used_map %]
+ [% map_html %]
+ </div>
+ <div id="side">
+[% ELSE %]
+ <div id="skipped-map">
+[% END %]
+
+<h1>[% loc('Reporting a problem') %]</h1>
+
+[% IF report.used_map %]
+ [% IF partial_token %]
+ <p id="unknown">[% loc('Please note your report has <strong>not yet been sent</strong>. Choose a category and add further information below, then submit.') %]</p>
+ [% END %]
+<p>[% loc('You have located the problem at the point marked with a purple pin on the map. If this is not the correct location, simply click on the map again. ') %]</p>
+[% END %]
+
+[% IF area_ids_to_list.size == 0 %]
+ [% INCLUDE 'report/new/no_councils_text.html' %]
+[% ELSIF area_ids_to_list.size == all_councils.size %]
+ [% INCLUDE 'report/new/all_councils_text.html' %]
+[% ELSE %]
+ [% INCLUDE 'report/new/some_councils_text.html' %]
+[% END %]
+
+<p>
+[% IF skipped %]
+ [% loc('Please fill in the form below with details of the problem, and describe the location as precisely as possible in the details box.') %]
+[% ELSE %]
+ [% INCLUDE 'report/new/fill_in_details_text.html' %]
+[% END %]
+</p>
+
+[% INCLUDE 'errors.html' %]
+
+<div id="problem_form">
+
+[% INCLUDE 'report/new/form_heading.html' %]
+
+<div id="fieldset">
+
+
+
+
+
+[% IF field_errors.council %]
+ <div class='form-error'>[% field_errors.council %]</div>
+[% END %]
+
+[% IF category_options.size %]
+ [% IF field_errors.category %]
+ <div class='form-error'>[% field_errors.category %]</div>
+ [% END %]
+
+ <div class="form-field">
+ <label for="form_category">[% category_label | html %]</label>
+ <select name="category" id="form_category">
+ [%- FOREACH cat_op IN category_options %]
+ <option value="[% cat_op | html %]"[% ' selected' IF report.category == cat_op %]>[% cat_op | html %]</option>
+ [%- END %]
+ </select>
+ </div>
+[% END %]
+
+[% IF field_errors.title %]
+ <div class='form-error'>[% field_errors.title %]</div>
+[% END %]
+
+<div class="form-field">
+ <label for="form_title">[% loc('Subject:') %]</label>
+ <input type="text" value="[% report.title | html %]" name="title" id="form_title" size="25">
+</div>
+
+[% IF field_errors.detail %]
+ <div class='form-error'>[% field_errors.detail %]</div>
+[% END %]
+
+<div class="form-field">
+ <label for="form_detail">[% loc('Details:') %]</label>
+ <textarea name="detail" id="form_detail" rows="7" cols="26">[% report.detail | html %]</textarea>
+</div>
+
+[% IF c.cobrand.allow_photo_upload %]
+ [% IF field_errors.photo %]
+ <div class='form-error'>[% field_errors.photo %]</div>
+ [% END %]
+
+ <div class='form-field'>
+ [% IF upload_fileid || report.photo %]
+ <p>[% loc('You have already attached a photo to this report, attaching another one will replace it.') %]</p>
+ [% IF upload_fileid %]
+ <input type="hidden" name="upload_fileid" value="[% upload_fileid %]" />
+ [% END %]
+ [% IF report.photo %]
+ <img align="right" src="/photo?id=[% report.id %]" hspace="5">
+ [% END %]
+ [% END %]
+
+ <label for="form_photo">[% loc('Photo:') %]</label>
+ <input type="file" name="photo" id="form_photo" >
+ </div>
+[% END %]
+
+[% IF field_errors.name %]
+ <div class='form-error'>[% field_errors.name %]</div>
+[% END %]
+
+<div class='form-field'>
+ <label for="form_name">[% loc('Name:') %]</label>
+ <input type="text" value="[% report.name | html %]" name="name" id="form_name" size="25">
+</div>
+
+
+<div class="checkbox">
+
+ [%# if there is nothing in the name field then set check box as default on form %]
+ <input type="checkbox" name="may_show_name" id="form_may_show_name" value="1"[% ' checked' IF !report.anonymous || !report.name %]>
+
+ [% IF c.cobrand.moniker == 'emptyhomes' %]
+ <label for="form_may_show_name">[% loc('Can we show your name on the site?') %]</label>
+ [% ELSE %]
+ <label for="form_may_show_name">[% loc('Can we show your name publicly?') %]</label>
+ [% END %]
+ <small>[% loc('(we never show your email address or phone number)') %]</small>
+</div>
+
+[% IF field_errors.email %]
+ <div class='form-error'>[% field_errors.email %]</div>
+[% END %]
+
+<div class="form-field">
+ <label for="form_email">[% loc('Email:') %]</label>
+ <input type="text" value="[% report_user.email | html %]" name="email" id="form_email" size="25">
+</div>
+
+<div>
+ <label for="form_phone">[% loc('Phone:') %]</label>
+ <input type="text" value="[% report_user.phone | html %]" name="phone" id="form_phone" size="15">
+ <small>[% loc('(optional)') %]</small>
+</div>
+
+[% INCLUDE 'report/new/notes.html' %]
+
+[% IF partial_token %]
+ <input type="hidden" name="partial" value="[% partial_token.token %]">
+[% END %]
+
+<p id="problem_submit">
+ <input type="hidden" name="submit_problem" value="1">
+ <input type="submit" value="[% loc('Submit') %]">
+</p>
+
+</div>
+</div>
+
+</div>
+</form>
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/report/new/fill_in_details_text.html b/templates/web/default/report/new/fill_in_details_text.html
new file mode 100644
index 000000000..44c60ed6e
--- /dev/null
+++ b/templates/web/default/report/new/fill_in_details_text.html
@@ -0,0 +1,10 @@
+[%
+ IF details != 'none';
+ loc('Please fill in details of the problem below. The council won\'t be able
+to help unless you leave as much detail as you can, so please describe the exact location of
+the problem (e.g. on a wall), what it is, how long it has been there, a description (and a
+photo of the problem if you have one), etc.');
+ ELSE;
+ loc('Please fill in details of the problem below.');
+ END;
+%]
diff --git a/templates/web/default/report/new/form_heading.html b/templates/web/default/report/new/form_heading.html
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/templates/web/default/report/new/form_heading.html
diff --git a/templates/web/default/report/new/no_councils_text.html b/templates/web/default/report/new/no_councils_text.html
new file mode 100644
index 000000000..f991e031f
--- /dev/null
+++ b/templates/web/default/report/new/no_councils_text.html
@@ -0,0 +1,20 @@
+
+
+<p>[%
+
+ nget(
+ "We do not yet have details for the council that covers this location.",
+ "We do not yet have details for the councils that cover this location.",
+ all_councils.size
+ );
+
+ loc("If you submit a problem here the subject and details of the problem will be public, but the problem will <strong>not</strong> be reported to the council.");
+
+ tprintf(
+ loc("You can help us by finding a contact email address for local problems for %s and emailing it to us at <a href='mailto:%s'>%s</a>."),
+ all_council_names.join( loc(' or ') ),
+ c.cobrand.contact_email,
+ c.cobrand.contact_email
+ );
+
+%]</p>
diff --git a/templates/web/default/report/new/notes.html b/templates/web/default/report/new/notes.html
new file mode 100644
index 000000000..38c8bfb7b
--- /dev/null
+++ b/templates/web/default/report/new/notes.html
@@ -0,0 +1,11 @@
+<p>[% loc("Please note:") %]</p>
+
+<ul>
+
+ <li>[% loc("We will only use your personal information in accordance with our <a href=\"/faq#privacy\">privacy policy.</a>") %]</li>
+ <li>[% loc("Please be polite, concise and to the point.") %]</li>
+ <li>[% loc("Please do not be abusive &mdash; abusing your council devalues the service for all users.") %]</li>
+ <li>[% loc("Writing your message entirely in block capitals makes it hard to read, as does a lack of punctuation.") %]</li>
+ <li>[% loc("Remember that FixMyStreet is primarily for reporting physical problems that can be fixed. If your problem is not appropriate for submission via this site remember that you can contact your council directly using their own website.") %]</li>
+
+</ul>
diff --git a/templates/web/default/report/new/report_import.html b/templates/web/default/report/new/report_import.html
new file mode 100644
index 000000000..7aa105afe
--- /dev/null
+++ b/templates/web/default/report/new/report_import.html
@@ -0,0 +1,92 @@
+[% INCLUDE 'header.html', title => 'External import' %]
+
+<h1>External import</h1>
+
+<p>You may inject problem reports into FixMyStreet programatically using this
+simple interface. Upon receipt, an email will be sent to the address given,
+with a link the user must click in order to check the details of their report,
+add any other information they wish, and then submit to the council.
+
+<p>This interface returns a plain text response; either <samp>SUCCESS</samp> if
+the report has been successfully received, or if not, a list of errors, one per
+line each starting with <samp>ERROR:</samp>.
+
+<p>You may submit the following information by POST to this URL
+(i.e. <samp>[% c.uri_for('/import') %]</samp> ):</p>
+
+<style type="text/css" media="screen">
+ input {
+ /* Hide the form elements - they are just here for simpler testing */
+ display: none;
+ }
+</style>
+
+<form method="POST" action="/import" enctype="multipart/form-data">
+
+<dl>
+ <dt>service</dt>
+ <dd>
+ <em>Required</em>.
+ Name of application/service using this interface.
+ <input type="text" name="service" />
+ </dd>
+
+ <dt>id</dt>
+ <dd>
+ Unique ID of a user/device, for possible future use.<br>
+ <small>(e.g. used by Flickr import to know which accounts to look at)</small>
+ <input type="text" name="id" />
+ </dd>
+
+ <dt>subject</dt>
+ <dd>
+ <em>Required</em>. Subject of problem report.
+ <input type="text" name="subject" />
+ </dd>
+
+ <dt>detail</dt>
+ <dd>
+ Main body and details of problem report.
+ <input type="text" name="detail" />
+ </dd>
+
+ <dt>name</dt>
+ <dd>
+ <em>Required</em>. Name of problem reporter.
+ <input type="text" name="name" />
+ </dd>
+
+ <dt>email</dt>
+ <dd>
+ <em>Required</em>. Email address of problem reporter.
+ <input type="text" name="email" />
+ </dd>
+
+ <dt>phone</dt>
+ <dd>
+ Telephone number of problem reporter.
+ <input type="text" name="phone" />
+ </dd>
+
+ <dt>easting / northing</dt>
+ <dt>lat / lon</dt>
+ <dd>
+ Location of problem report. You can either supply eastings/northings, or WGS84 latitude/longitude.
+ <input type="text" name="easting" />
+ <input type="text" name="northing" />
+ <input type="text" name="lat" />
+ <input type="text" name="lon" />
+ </dd>
+
+ <dt>photo</dt>
+ <dd>
+ Photo of problem (JPEG only).
+ <input type="file" name="photo" />
+ </dd>
+</dl>
+
+<input type="submit" />
+
+</form>
+
+[% INCLUDE 'footer.html' %] \ No newline at end of file
diff --git a/templates/web/default/report/new/some_councils_text.html b/templates/web/default/report/new/some_councils_text.html
new file mode 100644
index 000000000..042e89914
--- /dev/null
+++ b/templates/web/default/report/new/some_councils_text.html
@@ -0,0 +1,28 @@
+<p>
+[% loc('All the information you provide here will be sent to') %]
+
+[% FOREACH council_id IN area_ids_to_list %]
+ [% loc( ' or ') IF ! loop.first %]
+ <strong>[% all_councils.$council_id.name %]</strong>
+ [%- '.' IF loop.last %]
+[% END %]
+
+[%
+ loc('The subject and details of the problem will be public, plus your name if you give us permission.');
+%]
+[%
+ nget(
+ "We do <strong>not</strong> yet have details for the other council that covers this location.",
+ "We do <strong>not</strong> yet have details for the other councils that cover this location.",
+ missing_details_councils.size
+ );
+%]
+[%
+ tprintf(
+ loc("You can help us by finding a contact email address for local problems for %s and emailing it to us at <a href='mailto:%s'>%s</a>."),
+ missing_details_council_names.join( loc(' or ') ),
+ c.cobrand.contact_email,
+ c.cobrand.contact_email
+ );
+%]
+</p>
diff --git a/templates/web/default/report/photo.html b/templates/web/default/report/photo.html
new file mode 100644
index 000000000..451a0479c
--- /dev/null
+++ b/templates/web/default/report/photo.html
@@ -0,0 +1,6 @@
+[% IF c.cobrand.allow_photo_display && object.photo %]
+[% photo = object.get_photo_params %]
+<p[% ' align="center"' IF center %]>
+ <img alt="" height="[% photo.height %]" width="[% photo.width %]" src="[% photo.url %]">
+</p>
+[% END %]
diff --git a/templates/web/default/report/updates.html b/templates/web/default/report/updates.html
new file mode 100644
index 000000000..910430114
--- /dev/null
+++ b/templates/web/default/report/updates.html
@@ -0,0 +1,32 @@
+[% FOREACH update IN updates.all %]
+[% IF loop.first %]
+<div id="updates">
+ <h2 class="problem-update-list-header">[% loc('Updates') %]</h2>
+[% END %]
+ <div><div class="problem-update"><p><a name="update_[% update.id %]"></a><em>
+ [% IF update.anonymous || update.name == '' %]
+ [% tprintf( loc( 'Posted anonymously at %s' ), prettify_epoch( update.confirmed_local.epoch ) ) -%]
+ [%- ELSE %]
+ [% tprintf( loc( 'Posted by %s at %s' ), update.name, prettify_epoch( update.confirmed_local.epoch ) ) | html -%]
+ [%- END -%]
+ [%- c.cobrand.extra_update_meta_text(update) -%]
+ [%- ", " _ loc( 'marked as fixed' ) IF update.mark_fixed %]
+ [%- ", " _ loc( 'reopened' ) IF update.mark_open %]
+ </em></p>
+
+ [% IF c.cobrand.allow_update_reporting %]
+ <p>
+ <a rel="nofollow" class="unsuitable-problem" href="[% c.uri_for( '/contact?id=' _ update.problem_id _ ';update_id' _ update.id ) %]">[% loc('Offensive? Unsuitable? Tell us') %]</a>
+ </p>
+ [% END %]
+
+ </div>
+
+ <div class="update-text">
+ [% add_links( update.text ) | html_para %]
+
+ [% INCLUDE 'report/photo.html' object=update %]
+ </div>
+ </div>
+[% '</div>' IF loop.last %]
+[% END %]
diff --git a/templates/web/default/reports/council.html b/templates/web/default/reports/council.html
new file mode 100755
index 000000000..4067d0619
--- /dev/null
+++ b/templates/web/default/reports/council.html
@@ -0,0 +1,99 @@
+[% IF ward %]
+ [% name = "$ward.name, $council.name"
+ thing = loc('ward')
+ %]
+[% ELSE %]
+ [% name = council.name
+ thing = loc('council')
+ %]
+[% END %]
+
+[% INCLUDE 'header.html',
+ title = tprintf(loc('%s - Summary reports'), name)
+ context = 'reports'
+ rss = [ tprintf(loc('Problems within %s, FixMyStreet'), name), rss_url ]
+%]
+
+<p><a href="[% rss_url %]"><img align="right" src="/i/feed.png" width="16" height="16" title="[% loc('RSS feed') %]" alt="[% tprintf(loc('RSS feed of problems in this %s'), thing) %]" border="0" hspace="4"></a>
+
+[% IF c.cobrand.all_councils_report %]
+ [% tprintf( loc('This is a summary of all reports for one %s.'), thing ) %]
+[% ELSE %]
+ [% tprintf( loc('This is a summary of all reports for this %s.'), thing ) %]
+[% END %]
+
+[%# FIXME: It should link to council from a ward page, and should have list of wards on a council page. And a map?
+ The reason c.req.base/path is used below is that passing undef to uri_with
+ in a template actually passes "", and so the key still appears in the URL.
+%]
+
+[% IF c.req.parameters.all AND !c.cobrand.all_councils_report %]
+ [% tprintf( loc('You can <a href="%s">see less detail</a>.'), c.req.base _ c.req.path ) %]
+[% ELSIF !c.cobrand.all_councils_report %]
+ [% tprintf( loc('You can <a href="%s">see more details</a>.'), c.req.uri_with( { all = 1 } ) ) %]
+[% ELSIF c.req.parameters.all %]
+ [% tprintf( loc('You can <a href="%s">see less detail</a> or go back and <a href="/reports">show all councils</a>.'), c.req.base _ c.req.path ) %]
+[% ELSE %]
+ [% tprintf( loc('You can <a href="%s">see more details</a> or go back and <a href="/reports">show all councils</a>.'), c.req.uri_with( { all = 1 } ) ) %]
+[% END %]
+
+<h2>[% name %]</h2>
+
+<div id="col_problems">
+ [% INCLUDE column
+ title = loc('New problems')
+ problems = open.${council.id}.new
+ %]
+
+ [%# This doesn't really need a whole separate template %]
+ [% IF c.cobrand.moniker == 'emptyhomes' %]
+ [%
+ INCLUDE column
+ title = loc('Older problems')
+ problems = open.${council.id}.older.merge( open.${council.id}.unknown )
+ %]
+ [% ELSE %]
+ [% INCLUDE column
+ title = loc('Older problems')
+ problems = open.${council.id}.older
+ %]
+ [% INCLUDE column
+ title = loc('Old problems, state unknown')
+ problems = open.${council.id}.unknown
+ %]
+ [% END %]
+</div>
+
+<div id="col_fixed">
+ [% INCLUDE column
+ title = loc('Recently fixed')
+ problems = fixed.${council.id}.new
+ %]
+ [% INCLUDE column
+ title = loc('Old fixed')
+ problems = fixed.${council.id}.old
+ %]
+</div>
+
+[% INCLUDE 'footer.html' %]
+
+[% BLOCK column %]
+[% IF problems %]
+
+<h3>[% title %]</h3>
+
+<ul>
+[% FOREACH problem IN problems %]
+ <li><a href="[% c.uri_for(problem.url) %]">[% problem.title | html %]</a>
+ [% IF problem.councils.size > 1 %] <small>[% loc('(sent to both)') %]</small> [% END %]
+ [% IF c.cobrand.moniker != 'emptyhomes' %]
+ [% IF problem.councils.size == 0 %] <small>[% loc('(not sent to council)') %]</small> [% END %]
+ [% END %]
+ [% IF all %] <br><small>[% problem.detail %]</small> [% END %]
+ </li>
+[% END %]
+</ul>
+
+[% END %]
+[% END %]
+
diff --git a/templates/web/default/reports/index.html b/templates/web/default/reports/index.html
new file mode 100755
index 000000000..bb7824cad
--- /dev/null
+++ b/templates/web/default/reports/index.html
@@ -0,0 +1,35 @@
+[% INCLUDE 'header.html', title = loc('Summary reports') %]
+
+<p>
+[% loc('This is a summary of all reports on this site; select a particular council to see the reports sent there.') %]
+[% loc('Greyed-out lines are councils that no longer exist.') %]
+</p>
+
+<table cellpadding="3" cellspacing="1" border="0">
+<tr>
+<th>[% loc('Name') %]</th>
+<th>[% loc('New problems') %]</th>
+<th>[% loc('Older problems') %]</th>
+<th>[% loc('Old problems,<br>state unknown') %]</th>
+<th>[% loc('Recently fixed') %]</th>
+<th>[% loc('Older fixed') %]</th>
+</tr>
+
+[% FOREACH area IN areas_info_sorted %]
+<tr align="center"
+[%- IF area.generation_high == 10 %] class="gone"
+[%- ELSIF loop.count % 2 %] class="a"
+[%- END -%]
+>
+<td align="left"><a href="[% area.url %]">[% area.name %]</a></td>
+<td>[% open.${area.id}.new.size or 0 %]</td>
+<td>[% open.${area.id}.older.size or 0 %]</td>
+<td>[% open.${area.id}.unknown.size or 0 %]</td>
+<td>[% fixed.${area.id}.new.size or 0 %]</td>
+<td>[% fixed.${area.id}.old.size or 0 %]</td>
+</tr>
+[% END %]
+</table>
+
+[% INCLUDE 'footer.html' %]
+
diff --git a/templates/web/default/reports/ward.html b/templates/web/default/reports/ward.html
new file mode 100755
index 000000000..8b65ffb28
--- /dev/null
+++ b/templates/web/default/reports/ward.html
@@ -0,0 +1 @@
+[% INCLUDE reports/council.html %]
diff --git a/templates/web/default/static/about.html b/templates/web/default/static/about.html
new file mode 100644
index 000000000..9c319f051
--- /dev/null
+++ b/templates/web/default/static/about.html
@@ -0,0 +1,9 @@
+[% INCLUDE 'header.html', title => loc('About us') %]
+
+<h1>[% loc('About us') %]</h1>
+
+<h2>FixMyStreet.com</h2>
+
+[%# FIXME - put in blurb here %]
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/static/fun.html b/templates/web/default/static/fun.html
new file mode 100644
index 000000000..abe9d4028
--- /dev/null
+++ b/templates/web/default/static/fun.html
@@ -0,0 +1,35 @@
+[% INCLUDE header.html
+ title = 'Weird and Wonderful reports'
+%]
+
+<h1>Weird and Wonderful reports</h1>
+
+<p>Here are some of the best or strangest reports we&rsquo;ve seen on FixMyStreet.
+They&rsquo;ve all been fixed, and in one case could have saved lives!
+Do let us know if you find any more.</p>
+
+<ul style='list-style-type: none; margin:0; padding:0'>
+
+<li><img src='http://www.fixmystreet.com/photo?id=9468' align='right' hspace=8>
+ <h2>Dumped Piano (right)</h2>
+ <p>The reporter of this problem summed it up with their report,
+ which consisted solely of the one character &ldquo;!&rdquo;. &mdash;
+ <a href='http://www.fixmystreet.com/report/9468'>Problem report</a>
+
+<li><h2>Mad Seagull</h2>
+ <p>&ldquo;A seagull is attacking various cars within this road. He starts at around 05:45 every morning and continues until around 19:30. This causes a lot of noisy banging and wakes up children.&rdquo; &mdash;
+ <a href='http://www.fixmystreet.com/report/2722'>Problem report</a>
+
+<li><img src='http://www.fixmystreet.com/photo?id=6553' align='right' hspace=8>
+ <h2>Boxes full of cheese dumped (right)</h2>
+ <p>&ldquo;About a dozen boxes full of mozzarella cheese have been dumped opposite 3 rufford street. if it warms up we could have nasty road topping problem (seriously there is a lot of cheese)&rdquo; &mdash;
+ <a href='http://www.fixmystreet.com/report/6553'>Problem report</a>
+
+<li><h2>Dangerous Nivea Billboard</h2>
+ <p>&ldquo;The Nivea 'Oxygen is a wonderful thing' billboard here has a device on it releasing bubbles and foam. This is blowing into the road which is both distracting and dangerous to drivers. A large ball of foam hit my windscreen unexpectedly and nearly caused me to have an accident&rdquo; &mdash;
+ <a href='http://www.fixmystreet.com/report/7552'>Problem report</a>
+
+</ul>
+
+[% INCLUDE footer.html %]
+
diff --git a/templates/web/default/static/iphone.html b/templates/web/default/static/iphone.html
new file mode 100755
index 000000000..8f7992713
--- /dev/null
+++ b/templates/web/default/static/iphone.html
@@ -0,0 +1,52 @@
+[% INCLUDE header.html
+ title = 'FixMyStreet for iPhone screenshots'
+%]
+
+<h1>iPhone simulator simulator</h1>
+
+[%
+ SET screens = [
+ [ "iphone-1start.png", 'Click the image to progress through the flow of using the iPhone FixMyStreet application.
+ <br>When launched, the user&rsquo;s location automatically gets fetched&hellip;' ],
+ [ "iphone-2locfound.png", 'They want to take a photo.' ],
+ [ "iphone-pickpicture1.png", 'The simulator doesn&rsquo;t have a camera, so we&rsquo;re taken to the photo albums. Let&rsquo;s pick Hawaii.' ],
+ [ "iphone-pickpicture2.png", 'That red clouds photo looks nice.' ],
+ [ "iphone-pickpicture3.png", 'After any moving or scaling we want, we select the photo.' ],
+ [ "iphone-3picture.png", 'Okay, now we need to edit the summary of the report.' ],
+ [ "iphone-editsummary.png", 'Enter some text.' ],
+ [ "iphone-editsummary2.png", 'And done.' ],
+ [ "iphone-4subject.png", 'I haven&rsquo;t entered all my details yet, so that&rsquo;s next.' ],
+ [ "iphone-5details.png", 'Your details are remembered so you only have to enter them once.' ],
+ [ "iphone-6emailkeyboard.png", 'The iPhone has different keyboards, this is the email one.' ],
+ [ "iphone-5details.png", 'Right, we need to enter a name.' ],
+ [ "iphone-editname.png", 'Slightly different keyboard to the email one.' ],
+ [ "iphone-detailsdone.png", 'Okay, details entered.' ],
+ [ "iphone-allready.png", 'That&rsquo;s everything, hit Report!' ],
+ [ "iphone-7uploading.png", 'Uploading&hellip;' ],
+ [ "iphone-8response.png", 'The simulator always thinks it&rsquo;s in the US, which FixMyStreet won&rsquo;t like very much.' ],
+ [ "iphone-allready.png", 'Ah well, let&rsquo;s read the About page instead' ],
+ [ "iphone-9about.png", 'Donate? :)' ],
+];
+%]
+
+<script type="text/javascript">
+document.write('<style type="text/css">.vv { display: none; }</style>');
+function show(a) {
+ if (a==[% screens.size %]) b = 1;
+ else b = a+1;
+ document.getElementById('d' + a).style.display='none';
+ document.getElementById('d' + b).style.display='block';
+}
+</script>
+
+[% FOR screen IN screens %]
+
+ <div id='d[% loop.count %]'[% " class='vv'" IF NOT loop.first %]>
+ <p>[% screen.1 %]</p>
+ <p align='center'><a onclick='show([% loop.count %]);return false' href='#d[% loop.count + 1 %]'><img src='[% screen.0 %]' width=414 border=0 height=770></a></p>
+ </div>
+
+[% END %]
+
+[% INCLUDE footer.html %]
+
diff --git a/templates/web/default/static/posters.html b/templates/web/default/static/posters.html
new file mode 100644
index 000000000..77abbb5fc
--- /dev/null
+++ b/templates/web/default/static/posters.html
@@ -0,0 +1,43 @@
+[% INCLUDE header.html
+ title = 'Publicity material'
+%]
+
+[%
+ badge = '<a href="http://www.fixmystreet.com/"> <img align="left" hspace="5" src="http://www.fixmystreet.com/i/fms-badge.jpeg" alt="FixMyStreet - report, view or discuss local problems" border="0"></a>'
+%]
+
+<h1>Publicity Material</h1>
+
+<div style='float:left; width:50%'>
+ <p>Copy and paste the text below to add this badge to your site:</p>
+ [% badge %]
+ <textarea onclick="this.select()" cols=37 rows=5>[% badge | html %]</textarea>
+ <p><small>(thanks to Lincolnshire Council for the image)</small></p>
+</div>
+
+<div style='float:right; width:47%'>
+ <p>Here are some posters and flyers you can use to publicise FixMyStreet:</p>
+ <img hspace="5" src="/posters/poster.png" alt='Example poster'>
+ <h2>Posters</h2>
+ <ul>
+ <li><a href='/posters/fixmystreet-poster-a4.pdf'>A4, colour</a>
+ <li><a href='/posters/fixmystreet-poster-a4-bw.pdf'>A4, black and white</a>
+ <li><a href='/posters/fixmystreet-poster-a4-bw-low-ink.pdf'>A4, black and white, low ink</a>
+ <li><a href='/posters/fixmystreet-poster-a4-bw-outlined.pdf'>A4, black and white, outlined</a>
+ </ul>
+ <h2>Posters with tags</h2>
+ <ul>
+ <li><a href='/posters/fixmystreet-poster-tags.pdf'>A4, colour</a>
+ <li><a href='/posters/fixmystreet-poster-tags-bw.pdf'>A4, black and white</a>
+ <li><a href='/posters/fixmystreet-poster-tags-bw-low-ink.pdf'>A4, black and white, low ink</a>
+ <li><a href='/posters/fixmystreet-poster-tags-only.pdf'>A4, tags only</a>
+ </ul>
+ <h2>Flyers</h2>
+ <ul>
+ <li><a href='/posters/fixmystreet-flyers-colour.pdf'>4 x A6, colour</a>
+ <li><a href='/posters/fixmystreet-flyers-bw-outlined.pdf'>4 x A6, black and white, outlined</a>
+ <li><a href='/posters/fixmystreet-flyers-bw-low-ink.pdf'>4 x A6, black and white, low ink</a>
+ </ul>
+</div>
+
+[% INCLUDE footer.html %]
diff --git a/templates/web/default/tokens/abuse.html b/templates/web/default/tokens/abuse.html
new file mode 100644
index 000000000..d1b952621
--- /dev/null
+++ b/templates/web/default/tokens/abuse.html
@@ -0,0 +1,7 @@
+[% INCLUDE 'header.html', title => loc('Error') %]
+
+<h1>[% loc('Error') %]</h1>
+
+<p>[% loc('Sorry, there has been an error confirming your problem.') %]</p>
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/tokens/confirm_alert.html b/templates/web/default/tokens/confirm_alert.html
new file mode 100644
index 000000000..d23a658ec
--- /dev/null
+++ b/templates/web/default/tokens/confirm_alert.html
@@ -0,0 +1,17 @@
+[% INCLUDE 'header.html', title => loc('Local RSS feeds and email alerts') %]
+
+<h1>[% loc('Local RSS feeds and email alerts') %]</h1>
+
+<p>
+[% IF confirm_type == 'subscribe' %]
+ [% loc('You have successfully confirmed your alert.') %]
+[% ELSIF confirm_type == 'unsubscribe' %]
+ [% loc('You have successfully deleted your alert.') %]
+[% ELSIF confirm_type == 'created' %]
+ [% loc('You have successfully created your alert.') %]
+[% END %]
+</p>
+
+[% display_crosssell_advert( alert.user.email, alert.user.name ) %]
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/tokens/confirm_problem.html b/templates/web/default/tokens/confirm_problem.html
new file mode 100644
index 000000000..756958380
--- /dev/null
+++ b/templates/web/default/tokens/confirm_problem.html
@@ -0,0 +1,22 @@
+[% INCLUDE 'header.html', title => loc('Confirmation') %]
+
+<h1>[% loc('Confirmation') %]</h1>
+
+<p class="confirmed">
+[%
+ loc('You have successfully confirmed your problem');
+
+ IF problem.council;
+ loc(' and <strong>we will now send it to the council</strong>');
+ END;
+
+ tprintf(
+ loc( '. You can <a href="%s">view the problem on this site</a>.' ),
+ c.uri_for( '/report', problem.id )
+ );
+%]
+</p>
+
+[% display_crosssell_advert( problem.user.email, problem.name ) %]
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/tokens/confirm_update.html b/templates/web/default/tokens/confirm_update.html
new file mode 100644
index 000000000..56f04e12a
--- /dev/null
+++ b/templates/web/default/tokens/confirm_update.html
@@ -0,0 +1,20 @@
+[% INCLUDE 'header.html', title => loc('Confirmation') %]
+
+<h1>[% loc('Confirmation') %]</h1>
+
+<p class="confirmed">
+[% IF creator_fixed %]
+[%
+ tprintf(loc('Thank you &mdash; you can <a href="%s">view your updated problem</a> on the site.'), c.uri_for( '/report', problem_id ) );
+%]
+[% ELSE %]
+[%
+ tprintf( loc('You have successfully confirmed your update and you can now <a href="%s">view it on the site</a>.'), c.uri_for( '/report',
+ update.problem.id ) _ '#update_' _ update.id );
+%]
+[% END %]
+</p>
+
+[% display_crosssell_advert( update.user.email, update.name ) %]
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/tokens/error.html b/templates/web/default/tokens/error.html
new file mode 100644
index 000000000..e3fa6c170
--- /dev/null
+++ b/templates/web/default/tokens/error.html
@@ -0,0 +1,9 @@
+[% INCLUDE 'header.html', title => loc('Error') %]
+
+<h1>[% loc('Error') %]</h1>
+
+[% contact_url = c.uri_for('/contact'); %]
+
+<p>[% tprintf( loc('Thank you for trying to confirm your update or problem. We seem to have an error ourselves though, so <a href="%s">please let us know what went on</a> and we\'ll look into it.'), contact_url ) %]</p>
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/default/tracking_code.html b/templates/web/default/tracking_code.html
new file mode 100644
index 000000000..e5fc13793
--- /dev/null
+++ b/templates/web/default/tracking_code.html
@@ -0,0 +1,16 @@
+[% IF c.config.BASE_URL == "http://www.fixmystreet.com" %]
+<!-- Piwik -->
+<script type="text/javascript">
+var pkBaseURL = (("https:" == document.location.protocol) ? "https://piwik.mysociety.org/" : "http://piwik.mysociety.org/");
+document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
+</script><script type="text/javascript">
+try {
+var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", 8);
+piwikTracker.trackPageView();
+piwikTracker.enableLinkTracking();
+} catch( err ) {}
+</script><noscript><p><img src="http://piwik.mysociety.org/piwik.php?idsite=8" style="border:0" alt=""/></p></noscript>
+<!-- End Piwik Tag -->
+[% ELSE %]
+<!-- Tracking code not inserted as "[% c.config.BASE_URL %]" not "http://www.fixmystreet.com" -->
+[% END %]
diff --git a/templates/web/emptyhomes/contact/address.html b/templates/web/emptyhomes/contact/address.html
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/templates/web/emptyhomes/contact/address.html
diff --git a/templates/web/emptyhomes/contact/blurb.html b/templates/web/emptyhomes/contact/blurb.html
new file mode 100644
index 000000000..f1a2aa611
--- /dev/null
+++ b/templates/web/emptyhomes/contact/blurb.html
@@ -0,0 +1,3 @@
+<p>
+[% loc('We&rsquo;d love to hear what you think about this website. Just fill in the form. Please don&rsquo;t contact us about individual empty homes; use the box accessed from <a href="/">the front page</a>.') %]
+</p>
diff --git a/templates/website/cobrands/emptyhomes/faq-cy b/templates/web/emptyhomes/faq/faq-cy.html
index f0187859f..6d32845ca 100644
--- a/templates/website/cobrands/emptyhomes/faq-cy
+++ b/templates/web/emptyhomes/faq/faq-cy.html
@@ -1,3 +1,5 @@
+[% INCLUDE 'header.html', title => loc('Frequently Asked Questions') %]
+
<h1>Cwestiynau Cyffredin</h1>
<dl>
@@ -20,7 +22,7 @@ Hyd yn oed wedyn, gall y broses fod yn araf, yn enwedig os yw’r eiddo mewn cyf
Byddwn yn cysylltu â chi ddwywaith (mis a chwe mis ar ôl i chi roi gwybod am yr eiddo gwag), fel y gallwch ddweud wrthym beth sydd wedi digwydd. Os nad yw’r cyngor yn gwneud unrhyw beth, neu os ydych chi’n meddwl bod eu hymateb yn annigonol, byddwn yn rhoi cyngor i chi ar beth i’w wneud nesaf.</p> <p>
Os taw’r llywodraeth neu un o’i hasiantaethau sy’n berchen ar yr eiddo gwag, nid oes gan gynghorau unrhyw bŵer i helpu’n aml. Fodd bynnag, mae’n bosibl y byddwch yn gallu gweithredu’ch hunan yn uniongyrchol gan ddefnyddio PROD:
<a href="http://www.emptyhomes.com/usefulinformation/policy_docs/prods.html">http://www.emptyhomes.com/usefulinformation/policy_docs/prods.html</a>
-</dd>,
+</dd>
<dt>A fydd adrodd am eiddo gwag yn gwneud unrhyw wahaniaeth?</dt>
<dd><p>Bydd. Gall cynghorau wneud gwahaniaeth gwirioneddol, ond mae ganddynt lawer o bethau i’w gwneud. Bydd llawer o gynghorau ddim ond yn delio ag eiddo gwag sydd wedi cael eu hysbysu iddyn nhw. Os nad yw pobl yn rhoi gwybod am eiddo gwag, mae’n bosibl iawn y daw cynghorau i’r casgliad bod meysydd gwaith eraill yn fwy pwysig.</p> <p>
Mae dros 840,000 o dai gwag yn y DU. Mae’r Asiantaeth Tai Gwag yn amcangyfrif bod dros hanner y rhain yn wag heb angen. Effaith hyn yw gostwng sylweddol yn y stoc tai sydd ar gael, gan fwydo argyfwng tai’r DU. Sgil-effaith y gwastraff hwn yw bod llawer mwy o bwysau’n cael ei roi ar dir adeiladau gan fod mwy o dai’n cael eu hadeiladu i ateb y diffyg. Mae’r Asiantaeth Tai Gwag yn amcangyfrif y byddai dod â dim ond chwarter tai gwag y DU yn ôl mewn defnydd yn rhoi cartrefi i 700,000 o bobl, yn arbed 160 cilometr sgwâr o dir ac yn arbed 10 miliwn tunnell fetrig o CO<sub>2</sub> dros adeiladu’r un nifer o dai newydd.
@@ -57,3 +59,5 @@ Os taw’r llywodraeth neu un o’i hasiantaethau sy’n berchen ar yr eiddo gwa
href="http://www.m247.com/">M247</a> (sydd mor garedig â gwesteia’n holl weinyddion).
Rhowch wybod i ni os ydym wedi hepgor unrhyw un.</dd>
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/website/cobrands/emptyhomes/faq-en-gb b/templates/web/emptyhomes/faq/faq-en-gb.html
index b4c4423c3..1da62a41d 100755
--- a/templates/website/cobrands/emptyhomes/faq-en-gb
+++ b/templates/web/emptyhomes/faq/faq-en-gb.html
@@ -1,3 +1,5 @@
+[% INCLUDE 'header.html', title => loc('Frequently Asked Questions') %]
+
<h1>Frequently Asked Questions</h1>
<dl>
<dt>What is this site for?</dt>
@@ -47,7 +49,7 @@ If the empty home is owned by the government or one its agencies, councils are
often powerless to help. However you might be able to take action directly
yourself using a PROD:
<a href="http://www.emptyhomes.com/usefulinformation/policy_docs/prods.html">http://www.emptyhomes.com/usefulinformation/policy_docs/prods.html</a>
-</dd>,
+</dd>
<dt>Will reporting an empty home make any difference?</dt>
<dd><p>Yes. Councils can make a real difference, but they have lots of
things to do. Many councils only deal with empty homes that are reported to
@@ -108,3 +110,5 @@ our servers).
Let us know if we&rsquo;ve missed anyone.</dd>
</dl>
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/website/cobrands/emptyhomes/footer b/templates/web/emptyhomes/footer.html
index 011773e5b..e9a70de4f 100644
--- a/templates/website/cobrands/emptyhomes/footer
+++ b/templates/web/emptyhomes/footer.html
@@ -19,7 +19,7 @@ Email: <a href="mailto:info&#64;emptyhomes.com">info&#64;emptyhomes.com</a>
<div>
<i>in Wales:</i><br>
Shelter Cymru<br>
-25 Walter Road<br>Swansea
+[% lang_code == 'cy' ? '25 Heol Walter<br>Abertawe' : '25 Walter Road<br>Swansea' %]
<br>SA1 5NN<br>
<a href="mailto:emptyhomes&#64;sheltercymru.org.uk">emptyhomes&#64;sheltercymru.org.uk</a>
</div>
@@ -36,6 +36,8 @@ Tel: 0344 515 2461<br>
</div>
+[% INCLUDE 'debug_footer.html' %]
+
</body>
</html>
diff --git a/templates/web/emptyhomes/header.html b/templates/web/emptyhomes/header.html
new file mode 100644
index 000000000..08d1b1a05
--- /dev/null
+++ b/templates/web/emptyhomes/header.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="[% lang_code %]">
+<head>
+
+[% INCLUDE 'common_header_tags.html' %]
+
+<style type="text/css">
+@import "/css/core.css";
+@import "/cobrands/emptyhomes/css.css";
+</style>
+<!--[if LT IE 7]>
+<style type="text/css">@import url("/css/ie6.css");</style>
+<![endif]-->
+
+</head>
+<body>
+
+<div id="header">
+ <a href="http://www.emptyhomes.com/"><img id="eha-logo" border="0" src="/i/eha-logo.jpeg" alt="Empty Homes Agency" width="91" height="71" align="left"></a>
+ <div>
+ <small><em>in conjunction with</em></small>
+ <br>
+ <a href="http://www.sheltercymru.org.uk/"><img border="0" src="/i/Sheltercymru47s.png" alt="Shelter Cymru" width="80" height="33" align="bottom"></a>
+ <a href="http://scotland.shelter.org.uk/housing_issues/more_homes/empty_homes"><img border="0" src="/i/SEHPlogo-bwS.png" alt="Scottish Empty Homes Partnership" width="131" height="40" align="bottom"></a>
+ </div>
+</div>
+
+<div id="navigation">
+ <ul>
+ <li><a href="/">[% loc("Report a problem") %]</a></li>
+ <li><a href="/reports">[% loc("All reports") %]</a></li>
+ <li><a href="/alert">[% loc("Local alerts") %]</a></li>
+ <li><a href="/faq">[% loc("Help") %]</a></li>
+ <li><a href="/about">[% loc('About us') %]</a></li>
+ [% IF lang_code == 'en-gb' %]
+ <li><a href="http://cy.[% c.cobrand.base_host %][% c.req.uri.path_query %]">Cymraeg</a></li>
+ [% ELSE %]
+ <li><a href="http://en.[% c.cobrand.base_host %][% c.req.uri.path_query %]">English</a></li>
+ [% END %]
+ </ul>
+</div>
+
+<div id="wrapper">
+<div id="mysociety">
+
+ [% INCLUDE 'debug_header.html' %]
+
diff --git a/templates/web/emptyhomes/report/new/all_councils_text.html b/templates/web/emptyhomes/report/new/all_councils_text.html
new file mode 100644
index 000000000..8bdad1c1f
--- /dev/null
+++ b/templates/web/emptyhomes/report/new/all_councils_text.html
@@ -0,0 +1,8 @@
+<p>
+[%
+ tprintf(
+ loc('All the information you provide here will be sent to <strong>%s</strong>. On the site, we will show the subject and details of the problem, plus your name if you give us permission.'),
+ all_council_names.join( '</strong>' _ loc(' or ') _ '<strong>' )
+ );
+%]
+</p>
diff --git a/templates/web/emptyhomes/report/new/fill_in_details_text.html b/templates/web/emptyhomes/report/new/fill_in_details_text.html
new file mode 100644
index 000000000..b557a9942
--- /dev/null
+++ b/templates/web/emptyhomes/report/new/fill_in_details_text.html
@@ -0,0 +1,6 @@
+[% loc('Please fill in details of the empty property below, saying what type of
+property it is e.g. an empty home, block of flats, office etc. Tell us
+something about its condition and any other information you feel is relevant.
+There is no need for you to give the exact address. Please be polite, concise
+and to the point; writing your message entirely in block capitals makes it hard
+to read, as does a lack of punctuation.') %]
diff --git a/templates/web/emptyhomes/report/new/form_heading.html b/templates/web/emptyhomes/report/new/form_heading.html
new file mode 100644
index 000000000..911804a58
--- /dev/null
+++ b/templates/web/emptyhomes/report/new/form_heading.html
@@ -0,0 +1 @@
+<h2>[% loc('Empty property details form') %]</h2>
diff --git a/templates/web/emptyhomes/report/new/no_councils_text.html b/templates/web/emptyhomes/report/new/no_councils_text.html
new file mode 100644
index 000000000..dbe9cd510
--- /dev/null
+++ b/templates/web/emptyhomes/report/new/no_councils_text.html
@@ -0,0 +1,10 @@
+<p>
+[%
+ nget(
+ "We do not yet have details for the council that covers this location.",
+ "We do not yet have details for the councils that cover this location.",
+ all_councils.size
+ );
+%]
+[% loc("If you submit a report here it will be left on the site, but not reported to the council &ndash; please still leave your report, so that we can show to the council the activity in their area."); %]
+</p>
diff --git a/templates/web/emptyhomes/report/new/notes.html b/templates/web/emptyhomes/report/new/notes.html
new file mode 100644
index 000000000..d355cc1ba
--- /dev/null
+++ b/templates/web/emptyhomes/report/new/notes.html
@@ -0,0 +1 @@
+[%# This file deliberately left empty - there are no notes for EmptyHomes %] \ No newline at end of file
diff --git a/templates/web/emptyhomes/reports/index.html b/templates/web/emptyhomes/reports/index.html
new file mode 100755
index 000000000..003954f19
--- /dev/null
+++ b/templates/web/emptyhomes/reports/index.html
@@ -0,0 +1,33 @@
+[% INCLUDE 'header.html', title = loc('Summary reports') %]
+
+<p>
+[% loc('This is a summary of all reports on this site; select a particular council to see the reports sent there.') %]
+[% loc('Greyed-out lines are councils that no longer exist.') %]
+</p>
+
+<table cellpadding="3" cellspacing="1" border="0">
+<tr>
+<th>[% loc('Name') %]</th>
+<th>[% loc('New problems') %]</th>
+<th>[% loc('Older problems') %]</th>
+<th>[% loc('Recently fixed') %]</th>
+<th>[% loc('Older fixed') %]</th>
+</tr>
+
+[% FOREACH area IN areas_info_sorted %]
+<tr align="center"
+[%- IF area.generation_high == 10 %] class="gone"
+[%- ELSIF loop.count % 2 %] class="a"
+[%- END -%]
+>
+<td align="left"><a href="[% area.url %]">[% area.name %]</a></td>
+<td>[% open.${area.id}.new.size or 0 %]</td>
+<td>[% (open.${area.id}.older.size or 0) + (open.${area.id}.unknown.size or 0) %]</td>
+<td>[% fixed.${area.id}.new.size or 0 %]</td>
+<td>[% fixed.${area.id}.old.size or 0 %]</td>
+</tr>
+[% END %]
+</table>
+
+[% INCLUDE 'footer.html' %]
+
diff --git a/templates/web/emptyhomes/static/about.html b/templates/web/emptyhomes/static/about.html
new file mode 100644
index 000000000..880f69b98
--- /dev/null
+++ b/templates/web/emptyhomes/static/about.html
@@ -0,0 +1,43 @@
+[% INCLUDE 'header.html', title => loc('About us') %]
+
+<h1>[% loc('About us') %]</h1>
+
+<div style="float: left; width: 48%;">
+
+ <h2>[% loc('The Empty Homes Agency') %]</h2>
+
+ <p>[% loc('The Empty Homes agency is an independent campaigning charity. We
+ are not part of government, and have no formal links with local councils
+ although we work in cooperation with both. We exist to highlight the waste
+ of empty property and work with others to devise and promote sustainable
+ solutions to bring empty property back into use. We are based in London but
+ work across England. We also work in partnership with other charities across
+ the UK.') %]</p>
+
+</div>
+
+<div style="float: right; width:48%;">
+
+ <h2>[% loc('Shelter Cymru') %]</h2>
+
+ <p>[% loc('Shelter Cymru is Wales&rsquo; people and homes charity and wants
+ everyone in Wales to have a decent home. We believe a home is a fundamental
+ right and essential to the health and well-being of people and communities.
+ We work for people in housing need. We have offices all over Wales and
+ prevent people from losing their homes by offering free, confidential and
+ independent advice. When necessary we constructively challenge on behalf of
+ people to ensure they are properly assisted and to improve practice and
+ learning. We believe that bringing empty homes back into use can make a
+ significant contribution to the supply of affordable homes in Wales.') %]
+
+ <a href="http://www.sheltercymru.org.uk/shelter/advice/pdetail.asp?cat=20">
+ [% loc('Further information about our work on empty homes.') %]
+ </a>
+
+ </p>
+
+</div>
+
+<br clear="both">
+
+[% INCLUDE 'footer.html' %] \ No newline at end of file
diff --git a/templates/web/emptyhomes/tokens/confirm_problem.html b/templates/web/emptyhomes/tokens/confirm_problem.html
new file mode 100644
index 000000000..7bcf8acdf
--- /dev/null
+++ b/templates/web/emptyhomes/tokens/confirm_problem.html
@@ -0,0 +1,42 @@
+[% INCLUDE 'header.html', title => loc('Confirmation') %]
+
+<h1>[% loc('Confirmation') %]</h1>
+
+[% IF problem.council %]
+ <p>[%
+ loc(
+ 'Thank you for reporting an empty property on ReportEmptyHomes.com. We have emailed the lead officer for empty homes in the council responsible with details, and asked them to do whatever they can to get the empty property back into use as soon as possible.'
+ )
+ %]</p>
+
+ <p>[%
+ loc(
+ 'It is worth noting however that the process can sometimes be slow, especially if the property is in very poor repair or the owner is unwilling to act. In most cases it can take six months or more before you can expect to see anything change and sometimes there may be considerable barries to a property being brought back into use. This doesn&rsquo;t mean the council isn&rsquo;t doing anything. We encourage councils to update the website so you can see what is happening. It may be a long process, but you reporting your concerns about this property to the council is a valuable first step.'
+ )
+ %]</p>
+
+ <p>[%
+ loc(
+ 'We may contact you periodically to ask if anything has changed with the property you reported.'
+ )
+ %]</p>
+
+ <p>[%
+ loc(
+ 'Thank you for using ReportEmptyHomes.com. Your action is already helping to resolve the UK&rsquo;s empty homes crisis.'
+ )
+ %]</p>
+[% ELSE %]
+ <p>[%
+ loc( 'Thank you for reporting this empty property on ReportEmptyHomes.com.
+At present the report cannot be sent through to the council for this area. We
+are working with councils to link them into the system so that as many areas
+as possible will be covered.')
+ %]</p>
+[% END %]
+
+<p>
+ <a href="[% c.uri_for( '/report', problem.id ) | html %]">[% loc('View your report') %]</a>.
+</p>
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/emptyhomes/tracking_code.html b/templates/web/emptyhomes/tracking_code.html
new file mode 100644
index 000000000..73526d3bd
--- /dev/null
+++ b/templates/web/emptyhomes/tracking_code.html
@@ -0,0 +1,12 @@
+<!-- Piwik -->
+<script type="text/javascript">
+var pkBaseURL = (("https:" == document.location.protocol) ? "https://piwik.mysociety.org/" : "http://piwik.mysociety.org/");
+document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
+</script><script type="text/javascript">
+try {
+var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", 12);
+piwikTracker.trackPageView();
+piwikTracker.enableLinkTracking();
+} catch( err ) {}
+</script><noscript><img width=1 height=1 src="http://piwik.mysociety.org/piwik.php?idsite=12" style="border:0" alt=""></noscript>
+<!-- End Piwik Tag -->
diff --git a/templates/web/fiksgatami/contact/address.html b/templates/web/fiksgatami/contact/address.html
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/templates/web/fiksgatami/contact/address.html
diff --git a/templates/website/cobrands/fiksgatami/faq-nb b/templates/web/fiksgatami/faq/faq-nb.html
index 360ff8403..7281f1fe9 100644
--- a/templates/website/cobrands/fiksgatami/faq-nb
+++ b/templates/web/fiksgatami/faq/faq-nb.html
@@ -1,3 +1,5 @@
+[% INCLUDE 'header.html', title => loc('Frequently Asked Questions') %]
+
<h1>Ofte stilte spørsmål</h1>
<dl>
<dt>Hva er FiksGatami?</dt>
@@ -152,3 +154,5 @@
også gjøre tilgjengelig i kildekoden til slike prosjekter.</p></dd>
</dl>
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/fiksgatami/footer.html b/templates/web/fiksgatami/footer.html
new file mode 100644
index 000000000..a7f899af4
--- /dev/null
+++ b/templates/web/fiksgatami/footer.html
@@ -0,0 +1,23 @@
+</div></div>
+
+<h2 class="v">[% loc('Navigation') %]</h2>
+<ul id="navigation">
+<li><a href="/">[% loc("Report a problem") %]</a></li>
+<li><a href="/reports">[% loc("All reports") %]</a></li>
+<li><a href="[% c.uri_for('/alert', {pc => pc}) | html %]">[% loc("Local alerts") %]</a></li>
+<li><a href="/faq">[% loc("Help") %]</a></li>
+<li><a href="/contact">[% loc("Contact") %]</a></li>
+</ul>
+
+<div id="logo" align="center"><a href="http://www.nuug.no/">Foreningen NUUG</a></div>
+
+<p id="footer">
+ [% loc('Built by <a href="http://www.mysociety.org/">mySociety</a> and maintained by <a href="http://www.nuug.no/">NUUG</a>, using some <a href="http://github.com/mysociety/fixmystreet">clever</a>&nbsp;<a href="https://secure.mysociety.org/cvstrac/dir?d=mysociety/services/TilMa">code</a>.') %]
+</p>
+
+[% INCLUDE 'tracking_code.html' %]
+
+[% INCLUDE 'debug_footer.html' %]
+
+</body>
+</html>
diff --git a/templates/web/fiksgatami/header.html b/templates/web/fiksgatami/header.html
new file mode 100644
index 000000000..e0bf5b150
--- /dev/null
+++ b/templates/web/fiksgatami/header.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="[% lang_code %]">
+ <head>
+ [% INCLUDE 'common_header_tags.html' %]
+
+ <style type="text/css">
+ @import url("/css/core.css");
+ @import url("/cobrands/fiksgatami/css.css");
+ </style>
+ </head>
+ <body>
+
+ <div id="header">
+ <a href="/">[% loc('Fix<span id="my">My</span>Street') %]</a>
+ </div>
+
+ <div id="wrapper"><div id="mysociety">
+
+ [% INCLUDE 'debug_header.html' %]
diff --git a/templates/web/fiksgatami/reports/index.html b/templates/web/fiksgatami/reports/index.html
new file mode 100755
index 000000000..ab8ff6381
--- /dev/null
+++ b/templates/web/fiksgatami/reports/index.html
@@ -0,0 +1,34 @@
+[% INCLUDE 'header.html', title = loc('Summary reports') %]
+
+<p>
+[% loc('This is a summary of all reports on this site; select a particular council to see the reports sent there.') %]
+</p>
+
+<table cellpadding="3" cellspacing="1" border="0">
+<tr>
+<th>[% loc('Name') %]</th>
+<th>[% loc('New problems') %]</th>
+<th>[% loc('Older problems') %]</th>
+<th>[% loc('Old problems,<br>state unknown') %]</th>
+<th>[% loc('Recently fixed') %]</th>
+<th>[% loc('Older fixed') %]</th>
+</tr>
+
+[% FOREACH area IN areas_info_sorted %]
+[% NEXT IF area.id == 301 %]
+<tr align="center"
+[%- IF loop.count % 2 %] class="a"
+[%- END -%]
+>
+<td align="left"><a href="[% area.url %]">[% area.name %]</a></td>
+<td>[% open.${area.id}.new.size or 0 %]</td>
+<td>[% open.${area.id}.older.size or 0 %]</td>
+<td>[% open.${area.id}.unknown.size or 0 %]</td>
+<td>[% fixed.${area.id}.new.size or 0 %]</td>
+<td>[% fixed.${area.id}.old.size or 0 %]</td>
+</tr>
+[% END %]
+</table>
+
+[% INCLUDE 'footer.html' %]
+
diff --git a/templates/website/cobrands/southampton/footer b/templates/web/southampton/footer.html
index dac5d9c1a..dac5d9c1a 100644
--- a/templates/website/cobrands/southampton/footer
+++ b/templates/web/southampton/footer.html
diff --git a/templates/website/cobrands/southampton/header b/templates/web/southampton/header.html
index 4f47c92e5..a15054fb6 100644
--- a/templates/website/cobrands/southampton/header
+++ b/templates/web/southampton/header.html
@@ -1,17 +1,11 @@
<!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>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <script type="text/javascript" src="/yui/utilities.js"></script>
- <script type="text/javascript" src="/js.js"></script>
- {{ $map_js }}
- {{ $robots }}
+ [% INCLUDE 'common_header_tags.html' %]
- <title>{{ $title }}{{ $site_title }}</title>
<link rel="stylesheet" type="text/css" href="/css/core.css" />
<link rel="stylesheet" type="text/css" href="/cobrands/southampton/style.css" />
<link rel="stylesheet" type="text/css" href="/cobrands/southampton/css.css" />
- {{ $rss }}
</head>
<body>
<ul id="topMenu">
diff --git a/templates/website/cobrands/.gitignore b/templates/website/cobrands/.gitignore
deleted file mode 100644
index 911c95650..000000000
--- a/templates/website/cobrands/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/cities \ No newline at end of file
diff --git a/templates/website/cobrands/emptyhomes/header b/templates/website/cobrands/emptyhomes/header
deleted file mode 100644
index 3281430dd..000000000
--- a/templates/website/cobrands/emptyhomes/header
+++ /dev/null
@@ -1,41 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html lang="{{ $lang_code }}">
-<head>
-<title>{{ $title }}{{ $site_title }}</title>
-<meta http-equiv="content-type" content="text/html; charset=utf-8">
-<script type="text/javascript" src="/yui/utilities.js"></script>
-<script type="text/javascript" src="/js.js"></script>
-{{ $map_js }}
-<style type="text/css">
-@import "/css/core.css";
-@import "/css/cobrands/emptyhomes/emptyhomes.css";
-</style>
-<!--[if LT IE 7]>
-<style type="text/css">@import url("/css/ie6.css");</style>
-<![endif]-->
-{{ $rss }}
-</head>
-<body>
-
-<div id="header">
-<a href="http://www.emptyhomes.com/"><img id="eha-logo" border="0" src="/i/eha-logo.jpeg" alt="Empty Homes Agency" width="91" height="71" align="left"></a>
-<div>
-<small><em>in conjunction with</em></small><br>
-<a href="http://www.sheltercymru.org.uk/"><img border="0" src="/i/Sheltercymru47s.png" alt="Shelter Cymru" width="80" height="33" align="bottom"></a>
-<a href="http://scotland.shelter.org.uk/housing_issues/more_homes/empty_homes"><img border="0" src="/i/SEHPlogo-bwS.png" alt="Scottish Empty Homes Partnership" width="131" height="40" align="bottom"></a>
-</div>
-</div>
-
-<div id="navigation">
- <ul>
- <li><a href="/">{{ $report }}</a>
- <li><a href="/reports">{{ $reports }}</a></li>
- <li><a href="/alert">{{ $alert }}</a></li>
- <li><a href="/faq">{{ $faq }}</a></li>
- <li><a href="/about">{{ $about }}</a></li>
- <li><a href="{{ $lang_url }}">{{ $lang }}</a></li>
- </ul>
-</div>
-
-<div id="wrapper">
-<div id="mysociety">
diff --git a/templates/website/cobrands/fiksgatami/footer b/templates/website/cobrands/fiksgatami/footer
deleted file mode 100644
index e78e7d439..000000000
--- a/templates/website/cobrands/fiksgatami/footer
+++ /dev/null
@@ -1,16 +0,0 @@
-</div></div>
-<h2 class="v">{{ $navigation }}</h2>
-<ul id="navigation">
-<li><a href="/">{{ $report }}</a></li>
-<li><a href="/reports">{{ $reports }}</a></li>
-<li><a href="/alert{{ $pc }}">{{ $alerts }}</a></li>
-<li><a href="/faq">{{ $help }}</a></li>
-<li><a href="/contact">{{ $contact }}</a></li>
-</ul>
-
-{{ $orglogo }}
-
-<p id="footer">{{ $creditline }}</p>
-
-</body>
-</html>
diff --git a/templates/website/cobrands/fiksgatami/header b/templates/website/cobrands/fiksgatami/header
deleted file mode 100644
index 41ccabff9..000000000
--- a/templates/website/cobrands/fiksgatami/header
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html lang="{{ $lang_code }}">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <script type="text/javascript" src="/yui/utilities.js"></script>
- <script type="text/javascript" src="/js.js"></script>
- {{ $map_js }}
- {{ $robots }}
-
- <title>{{ $title }}{{ $site_title }}</title>
- <style type="text/css">
- @import url("/css/core.css");
- @import url("/cobrands/fiksgatami/css.css");
- </style>
- {{ $rss }}
- </head>
- <body>
- {{ $heading_element_start }}{{ $heading }}{{ $heading_element_end }}
- <div id="wrapper"><div id="mysociety">
diff --git a/templates/website/contact b/templates/website/contact
deleted file mode 100644
index b1103d3f1..000000000
--- a/templates/website/contact
+++ /dev/null
@@ -1,42 +0,0 @@
-<h1>{{ $header }}</h1>
-{{ $errors }}
-<form method="post" action="{{ $form_action }}">
-<input type="hidden" name="submit_form" value="1">
-
-{{ $intro }}
-{{ if ( $item_title ) {
- "<blockquote><h2>$item_title</h2><p>$item_meta</p><p>$item_body</p></blockquote>";
-
-} }}
-
-{{ $hidden_vals }}
-{{ if ( $field_errors{name}) {
- "<div class='form-error'>$field_errors{name}</div>";
-} }}
-<div class="form-field">
-<label for="form_name">{{ $label_name }}</label>
-<input type="text" name="name" id="form_name" value="{{ $input_h{name} }}" size="30"></div>
-
-{{ if ( $field_errors{email}) {
- "<div class='form-error'>$field_errors{email}</div>";
-} }}
-<div class="form-field">
-<label for="form_email">{{ $label_email }}</label>
-<input type="text" name="em" id="form_email" value="{{ $input_h{em} }}" size="30"></div>
-{{ if ( $field_errors{subject}) {
- "<div class='form-error'>$field_errors{subject}</div>";
-} }}
-<div class="form-field">
-<label for="form_subject">{{ $label_subject }}</label>
-<input type="text" name="subject" id="form_subject" value="{{ $input_h{subject} }}" size="30"></div>
-{{ if ( $field_errors{message}) {
- "<div class='form-error'>$field_errors{message}</div>";
-} }}
-<div class="form-field">
-<label for="form_message">{{ $label_message }}</label>
-<textarea name="message" id="form_message" rows="7" cols="50">{{ $input_h{message} }}</textarea></div>
-<div class="checkbox"><input type="submit" value="{{ $label_submit }}"></div>
-{{ $cobrand_form_elements }}
-</form>
-
-{{ $contact_details }}
diff --git a/templates/website/header b/templates/website/header
deleted file mode 100644
index b88d9b82c..000000000
--- a/templates/website/header
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html lang="{{ $lang_code }}">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <script type="text/javascript" src="/yui/utilities.js"></script>
- <script type="text/javascript" src="/js.js"></script>
- {{ $map_js }}
- {{ $robots }}
-
- <title>{{ $title }}{{ $site_title }}</title>
- <style type="text/css">@import url("/css/core.css"); @import url("/css/main.css");</style>
- <!--[if LT IE 7]>
- <style type="text/css">@import url("/css/ie6.css");</style>
- <![endif]-->
- {{ $rss }}
- </head>
- <body>
- {{ $heading_element_start }}{{ $heading }}{{ $heading_element_end }}
- <div id="wrapper"><div id="mysociety">
diff --git a/templates/website/map b/templates/website/map
deleted file mode 100755
index f54c68aba..000000000
--- a/templates/website/map
+++ /dev/null
@@ -1,28 +0,0 @@
-{{ $map }}
-
-<h1>{{ $heading_problems }}</h1>
-
-<p id="alert_links_area">
-<a id="email_alert" rel="nofollow" href="{{ $url_email }}">{{ $email_me }}</a>
-| <a href="{{ $url_rss }}" id="rss_alert"><span>{{ $rss_alt }}</span> <img src="/i/feed.png" width="16" height="16" title="{{ $rss_title }}" alt="{{ $rss_alt }}" border="0" style="vertical-align: top"></a>
-</p>
-
-{{ $errors }}
-
-<p id="text_map">{{ $text_to_report }} {{ $text_skip }}</p>
-
-<div id="nearby_lists">
-<h2>{{ $heading_on_around }}</h2>
-
-<ul id="current">
-{{ $reports_on_around }}
-</ul>
-
-<h2 id="closest_problems">{{ $heading_closest }}</h2>
-
-<ul id="current_near">
-{{ $reports_nearby }}
-</ul>
-
-</div>
-{{ $map_end }}
diff --git a/templates/website/problem b/templates/website/problem
deleted file mode 100644
index 4debc1d2e..000000000
--- a/templates/website/problem
+++ /dev/null
@@ -1,62 +0,0 @@
-{{ $map_start }}
-{{ $banner }}
-
-<h1>{{ $problem_title }}</h1>
-<p><em>{{ $problem_meta }}</em></p>
-{{ $problem_detail }}
-{{ $problem_photo }}
-
-<p align="right"><small>{{ $unsuitable }}</small></p>
-<p style="padding-bottom: 0.5em; border-bottom: dotted 1px #999999;" align="right">{{ $more_problems }}</p>
-
-<div id="alert_links">
-<a rel="nofollow" id="email_alert" href="{{ $alert_link }}">{{ $alert_text }}</a>
-
-<form action="{{ $form_alert_action }}" method="post" id="email_alert_box">
-<p>{{ $blurb }}</p>
-<label class="n" for="alert_rznvy">{{ $email_label }}</label>
-<input type="text" name="rznvy" id="alert_rznvy" value="{{ $input_h{rznvy} }}" size="30">
-<input type="hidden" name="id" value="{{ $input_h{id} }}">
-<input type="hidden" name="type" value="updates">
-<input type="submit" value="{{ $subscribe }}">
-{{ $cobrand_form_elements1 }}
-</form>
-&nbsp; <a href="{{ $rss_url }}"><img src="/i/feed.png" width="16" height="16" title="{{ $rss_title }}" alt="{{ $rss_alt }}" border="0" style="vertical-align: middle"></a>
-</div>
-
-{{ $problem_updates }}
-
-<div id="update_form">
-
-{{ $update_heading }}
-{{ $update_blurb }}
-
-{{ $errors }}
-
-<form method="post" action="{{ $form_action }}" name="updateForm" id="fieldset"{{ $enctype }}>
-<input type="hidden" name="submit_update" value="1">
-<input type="hidden" name="id" value="{{ $input_h{id} }}">
-<div><label for="form_name">{{ $name_label }}</label>
-<input type="text" name="name" id="form_name" value="{{ $input_h{name} }}" size="20"> {{ $optional }}</div>
-{{ if ( $field_errors{email}) {
- "<div class='form-error'>$field_errors{email}</div>";
-} }}
-<div class="form-field">
-<label for="form_rznvy">{{ $email_label }}</label>
-<input type="text" name="rznvy" id="form_rznvy" value="{{ $input_h{rznvy} }}" size="20"></div>
-{{ if ( $field_errors{update}) {
- "<div class='form-error'>$field_errors{update}</div>";
-} }}
-<div class="form-field">
-<label for="form_update">{{ $update_label }}</label>
-<textarea name="update" id="form_update" rows="7" cols="30">{{ $input_h{update} }}</textarea></div>
-<div class="checkbox">{{ $fixedline_box }} {{ $fixedline_label }}</div>
-{{ $photo_element }}
-<div class="checkbox"><input type="checkbox" name="add_alert" id="form_add_alert" value="1"{{ $add_alert_checked }}>
-<label for="form_add_alert">{{ $alert_label }}</label></div>
-<div class="checkbox"><input type="submit" id="update_post" value="{{ $post_label }}"></div>
-{{ $cobrand_form_elements }}
-</form>
-</div>
-
-{{ $map_end }}
diff --git a/templates/website/questionnaire b/templates/website/questionnaire
deleted file mode 100644
index 869552c4b..000000000
--- a/templates/website/questionnaire
+++ /dev/null
@@ -1,34 +0,0 @@
-{{ $map_start }}
-
-<h1>{{ $heading }}</h1>
-
-<form method="post" action="{{ $form_action }}" id="questionnaire"{{ $enctype }}>
-<input type="hidden" name="token" value="{{ $input_h{token} }}">
-
-{{ $blurb_eh }}
-
-<p>{{ $blurb_report }} {{ $blurb_report2 }}</p>
-
-{{ $errors }}
-
-<p>{{ $fixed_question }}</p>
-<p>
-<input type="radio" name="been_fixed" id="been_fixed_yes" value="Yes"{{ $been_fixed{yes} }}>
-<label for="been_fixed_yes">{{ $yes }}</label>
-<input type="radio" name="been_fixed" id="been_fixed_no" value="No"{{ $been_fixed{no} }}>
-<label for="been_fixed_no">{{ $no }}</label>
-<input type="radio" name="been_fixed" id="been_fixed_unknown" value="Unknown"{{ $been_fixed{unknown} }}>
-<label for="been_fixed_unknown">{{ $dontknow }}</label>
-</p>
-
-{{ $ever_reported }}
-
-{{ $blurb_update }}
-<p><textarea name="update" style="max-width:90%" rows="7" cols="30">{{ $input_h{update} }}</textarea></p>
-{{ $photo_input }}
-
-{{ $another_questionnaire }}
-
-<p><input type="submit" name="submit" value="{{ $submit }}"></p> </form>
-
-{{ $map_end }}
diff --git a/templates/website/report-form b/templates/website/report-form
deleted file mode 100644
index fd6fc631d..000000000
--- a/templates/website/report-form
+++ /dev/null
@@ -1,68 +0,0 @@
-{{ $form_start }}
-{{ $page_heading }}
-
-{{ $text_located }}
-
-{{ $text_help }}
-
-{{ $errors }}
-
-<div id="problem_form">
-{{ $form_heading }}
-<div id="fieldset">
-
-
-{{ if ( $field_errors{category}) {
- "<div class='form-error'>$field_errors{category}</div>";
-} }}
-
-<div class="form-field">{{ $category }}</div>
-
-{{ if ( $field_errors{title}) {
- "<div class='form-error'>$field_errors{title}</div>";
-} }}
-<div class="form-field">
-<label for="form_title">{{ $subject_label }}</label>
-<input type="text" value="{{ $input_h{title} }}" name="title" id="form_title" size="25"></div>
-{{ if ( $field_errors{detail}) {
- "<div class='form-error'>$field_errors{detail}</div>";
-} }}
-<div class="form-field">
-<label for="form_detail">{{ $detail_label }}</label>
-
-<textarea name="detail" id="form_detail" rows="7" cols="26">{{ $input_h{detail} }}</textarea></div>
-
-{{ $partial_field }}
-
-{{ if ( $field_errors{photo}) {
- "<div class='form-error'>$field_errors{photo}</div>";
-} }}
-{{ $photo_field }}
-
-{{ if ( $field_errors{name}) {
- "<div class='form-error'>$field_errors{name}</div>";
-} }}
-<div class='form-field'>
-<label for="form_name">{{ $name_label }}</label>
-<input type="text" value="{{ $input_h{name} }}" name="name" id="form_name" size="25"></div>
-<div class="checkbox"><input type="checkbox" name="anonymous" id="form_anonymous" value="1"{{ $anon }}>
-<label for="form_anonymous">{{ $anonymous }}</label>
-<small>{{ $anonymous2 }}</small></div>
-{{ if ( $field_errors{email}) {
- "<div class='form-error'>$field_errors{email}</div>";
-} }}
-<div class="form-field">
-<label for="form_email">{{ $email_label }}</label>
-<input type="text" value="{{ $input_h{email} }}" name="email" id="form_email" size="25"></div>
-<div><label for="form_phone">{{ $phone_label }}</label>
-<input type="text" value="{{ $input_h{phone} }}" name="phone" id="form_phone" size="15">
-<small>{{ $optional }}</small></div>
-
-{{ $text_notes }}
-
-<p id="problem_submit"><input type="submit" name="submit_problem" value="{{ $submit_button }}"></p>
-</div>
-</div>
-
-{{ $map_end }}
-
diff --git a/templates/website/reports b/templates/website/reports
deleted file mode 100644
index 2519a0301..000000000
--- a/templates/website/reports
+++ /dev/null
@@ -1,12 +0,0 @@
-<p><a href="{{ $rss_url }}"><img align="right" src="/i/feed.png" width="16" height="16" title="{{ $rss_title }}" alt="{{ $rss_alt }}" border="0" hspace="4"></a> {{ $summary_title }} {{ $summary_line }}
-
-<h2>{{ $name }}</h2>
-
-<div id="col_problems">
-{{ $col_problems }}
-</div>
-
-<div id="col_fixed">
-{{ $col_fixed }}
-</div>
-
diff --git a/templates/website/scambs-footer b/templates/website/scambs-footer
deleted file mode 100644
index 4a0a319cc..000000000
--- a/templates/website/scambs-footer
+++ /dev/null
@@ -1,19 +0,0 @@
-</div></div>
-
-</div>
-
-<div id="bottominfo">
- <ul>
- <li><script type="text/javascript">printPageXHTML();</script></li><li><img src="http://www.scambs.gov.uk/images/southcambridgeshiredistrictcouncil/icons/internet/email.gif" alt="" height="15" width="15"> <a href="http://www.scambs.gov.uk/friend.asp?page=">email this page</a></li><li><img src="http://www.scambs.gov.uk/images/southcambridgeshiredistrictcouncil/icons/internet/ask.gif" alt="" height="15" width="15"> <a href="http://www.scambs.gov.uk/question.asp?page=&amp;owner=39">ask a question</a></li>
- </ul>
-</div>
-
-<div style="clear: left;" id="footer">
- <div id="footertext">
- <a href="http://www.scambs.gov.uk/disclaimer.htm">Disclaimer</a> | <a href="http://www.scambs.gov.uk/privacy.htm">Privacy Statement</a> | <a href="http://www.scambs.gov.uk/accesskeys.htm">Access Keys / Accessibility</a> | <a href="http://www.scambs.gov.uk/CouncilAndDemocracy/Consultation/complaints_compliments.htm">Complaints &amp; Compliments Procedure</a>
- </div>
-</div>
-
-</div>
-</body>
-</html>
diff --git a/templates/website/scambs-header b/templates/website/scambs-header
deleted file mode 100644
index 326602de1..000000000
--- a/templates/website/scambs-header
+++ /dev/null
@@ -1,82 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html lang="en-gb">
-<head>
-<title>{{ $title }}Envirocrime - South Cambridgeshire District Council</title>
-<meta http-equiv="content-type" content="text/html; charset=utf-8">
-<script type="text/javascript" src="/yui/utilities.js"></script>
-<script type="text/javascript" src="/js.js"></script>
-<style type="text/css" media="screen, projection">
-@import "/css/core.css";
-@import "/css/main-scambs.css";
-@import "/css/scambs.css";
-</style>
-<!--[if LT IE 7]>
-<style type="text/css">@import url("/css/ie6-378.css");</style>
-<![endif]-->
-<link rel="stylesheet" type="text/css" href="http://www.scambs.gov.uk/system/css/print.css" media="print">
-<script type="text/javascript" src="http://www.scambs.gov.uk/system/js/external.js"></script>
-<!-- RSS -->
-</head>
-<body>
-
-<div class="test">
-
-<div id="top">
- <div id="topnav">
- <ul style="float: right;"><li style="float: left; padding-top: 2px;"><a href="#start" title="Accessibility: bypass navigation bar" accesskey="s">Skip to Content</a>&nbsp;|&nbsp;</li><li style="float: left; padding-top: 2px;"><a href="http://www.scambs.gov.uk/accesskeys.htm" title="Accessibility" accesskey="0">Accessibility</a>&nbsp;|&nbsp;</li><li style="float: left;"><a target="_blank" style="float: left;" href="http://spoxy4.insipio.com/generator/en/www.scambs.gov.uk/default.htm" class="ISI_REMOVE" accesskey="5" rel="external"><img style="float: left;" src="http://www.scambs.gov.uk/system/images/speakericon.gif" alt="Listen to this Page" height="20" width="23"><span style="padding: 2px 0pt 0pt 2px; display: block;">&nbsp;Listen to page</span><span class="hide"> (This link will open in a new window)</span></a></li></ul>
- </div>
-</div>
-
-<div id="masthead">
- <div id="bannerimage">
- <div id="logo"><a href="http://www.scambs.gov.uk/default.htm"><img src="http://www.scambs.gov.uk/system/images/scambs/banner_right.gif" alt="South Cambridgeshire District Council Logo" height="150" width="327"></a></div>
- </div>
-</div>
-
-<div id="header">
- <div id="headernav">
- <a href="http://www.scambs.gov.uk/" title="South Cambridgeshire District Council Home Page" accesskey="1">Home</a> |
- <a href="http://search2.openobjects.com/kbroker/cambridgeshire/scambs/a2z/search.a2z?letter=a&amp;action=browseEntries&amp;servlet=search.lsim&amp;provider=6" title="A to Z listing of council services" accesskey="3">A-Z</a> |
- <a href="http://www.scambs.gov.uk/siteindex/default.htm?flat=1" title="Site map">Site Map</a> |
- <a href="http://www.scambs.gov.uk/form_builder/default.asp?mode=10&amp;sid=33&amp;pid=64" title="Feedback" accesskey="4">Contact us</a> |
- <a href="http://www.scambs.gov.uk/events/" title="Listing of current events">Events</a> |
- <a href="http://www.scambs.gov.uk/pressreleases/" title="News releases">News releases</a>
- </div>
- <form id="search" action="http://search2.openobjects.com/kbroker/cambridgeshire/scambs/search/search.lsim" method="get">
- <noscript><div class="floatright"><input type="submit" value="Go" /></div></noscript>
- <div id="headersearch">
- <img src="http://www.scambs.gov.uk/system/images/scambs/search.gif" alt="Search the South Cambridgeshire website" height="21" width="32"><a href="http://search2.openobjects.com/kbroker/cambridgeshire/scambs/search/asearch.jsp" title="Search site using advance functions" accesskey="2">Advanced Search</a> | <label class="n" for="q">Search: <input size="12" class="form" name="qt" id="q" value="enter text " type="text"></label><input value="R" name="order" type="hidden">
- <input name="sr" value="0" type="hidden">
- <input name="nh" value="10" type="hidden">
- <input name="cs" value="iso-8859-1" type="hidden">
- <input name="sc" value="scambs" type="hidden">
- <input name="sm" value="0" type="hidden">
- <input name="sf" value="" type="hidden">
- <input name="mt" value="1" type="hidden">
- <input name="ha" value="1115" type="hidden">
- </div>
- </form>
-</div>
-
-<div id="breadcrumb">
- <div id="breadcrumbtext">
- <a title="Breadcrumb link: Home" href="http://www.scambs.gov.uk/">Home</a> &gt; <a href="/">Envirocrime</a>
- </div>
-</div>
-
-<div id="content">
- <a name="start" id="start"></a>
- <div id="left">
- <div id="leftnav">
- <ul>
- <li class="first"><a href="/">Report a problem</a>
- <li class="first">About
- <li class="first">Recent successes
- <li class="first">Name and shame
- <li class="first">Contact
- <li class="first">Help
- </ul>
- </div>
- </div>
- <!-- <div id="right"></div> --> <!-- OPTIONAL -->
- <div id="middle"><div id="maintext">
diff --git a/urls.txt b/urls.txt
new file mode 100644
index 000000000..5c9b25f34
--- /dev/null
+++ b/urls.txt
@@ -0,0 +1,12 @@
+This is a list of some of the urls currently served:
+
+homepage: /
+postcode search: /?pc=SW1A+1AA
+placename: /?pc=Westminster
+street (ambiguous): /?pc=St+Margaret+St
+street (exact): /?pc=St+Margaret+St%2c+Westminster%2c+London+SW1A+2
+
+all reports: /reports
+council reports: /reports/Aberdeen
+individual report: /report/173526
+
diff --git a/web-admin/.gitignore b/web-admin/.gitignore
deleted file mode 100644
index 973f53659..000000000
--- a/web-admin/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/_Inline
diff --git a/web-admin/index.cgi b/web-admin/index.cgi
deleted file mode 100755
index aaf4e3651..000000000
--- a/web-admin/index.cgi
+++ /dev/null
@@ -1,971 +0,0 @@
-#!/usr/bin/perl -w
-#
-# index.cgi
-#
-# Administration interface for FixMyStreet
-#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
-# Email: francis@mysociety.org; WWW: http://www.mysociety.org/
-#
-# $Id: index.cgi,v 1.88 2010-01-20 12:55:46 louise Exp $
-#
-
-my $rcsid = ''; $rcsid .= '$Id: index.cgi,v 1.88 2010-01-20 12:55:46 louise Exp $';
-
-use strict;
-
-# Horrible boilerplate to set up appropriate library paths.
-use FindBin;
-use lib "$FindBin::Bin/../perllib";
-use lib "$FindBin::Bin/../commonlib/perllib";
-use Encode;
-use POSIX qw(strftime strcoll);
-use Digest::MD5 qw(md5_hex);
-
-use Page;
-use mySociety::Config;
-use mySociety::DBHandle qw(dbh select_all);
-use mySociety::MaPit;
-use mySociety::VotingArea;
-use mySociety::Web qw(NewURL ent);
-
-BEGIN {
- mySociety::Config::set_file("$FindBin::Bin/../conf/general");
- mySociety::DBHandle::configure(
- Name => mySociety::Config::get('BCI_DB_NAME'),
- User => mySociety::Config::get('BCI_DB_USER'),
- Password => mySociety::Config::get('BCI_DB_PASS'),
- Host => mySociety::Config::get('BCI_DB_HOST', undef),
- Port => mySociety::Config::get('BCI_DB_PORT', undef)
- );
-}
-
-
-=item get_token Q
-
-Generate a token based on user and secret
-
-=cut
-sub get_token {
- my ($q) = @_;
- my $secret = scalar(dbh()->selectrow_array('select secret from secret'));
- my $token = md5_hex(($q->remote_user() . $secret));
- return $token;
-}
-
-=item allowed_pages Q
-
-Return a hash of allowed pages, keyed on page param. The values of the hash
-are arrays of the form [link_text, link_order]. Pages without link_texts
-are not to be included in the main admin menu.
-=cut
-sub allowed_pages($) {
- my ($q) = @_;
- my $cobrand = Page::get_cobrand($q);
- my $pages = Cobrand::admin_pages($cobrand);
- if (!$pages) {
- $pages = {
- 'summary' => [_('Summary'), 0],
- 'councilslist' => [_('Council contacts'), 1],
- 'reports' => [_('Search Reports'), 2],
- 'timeline' => [_('Timeline'), 3],
- 'questionnaire' => [_('Survey Results'), 4],
- 'councilcontacts' => [undef, undef],
- 'counciledit' => [undef, undef],
- 'report_edit' => [undef, undef],
- 'update_edit' => [undef, undef],
- };
- }
- return $pages;
-}
-
-sub html_head($$) {
- my ($q, $title) = @_;
- my $ret = $q->header(-type => 'text/html', -charset => 'utf-8');
- my $site_title = _('FixMyStreet administration');
- $ret .= <<END;
-<html>
-<head>
-<title>$title - $site_title</title>
-<style type="text/css">
-dt { clear: left; float: left; font-weight: bold; }
-dd { margin-left: 8em; }
-.hidden { color: #666666; }
-</style>
-</head>
-<body>
-END
- my $pages = allowed_pages($q);
- my @links = sort {$pages->{$a}[1] <=> $pages->{$b}[1]} grep {$pages->{$_}->[0] } keys %$pages;
- $ret .= $q->p(
- $q->strong(_("FixMyStreet admin:")),
- map { $q->a( { href => NewURL($q, page => $_) }, $pages->{$_}->[0]) } @links
- );
-
- return $ret;
-}
-
-sub html_tail($) {
- my ($q) = @_;
- return <<END;
-</body>
-</html>
-END
-}
-
-sub fetch_data {
-}
-
-# admin_summary CGI
-# Displays general summary of counts.
-sub admin_summary ($) {
- my ($q) = @_;
- my $cobrand = Page::get_cobrand($q);
- print html_head($q, _("Summary"));
- print $q->h1(_("Summary"));
-
- my $contacts = Problems::contact_counts($cobrand);
- my %contacts = @$contacts;
- $contacts{0} ||= 0;
- $contacts{1} ||= 0;
- $contacts{total} = $contacts{0} + $contacts{1};
-
- my $comments = Problems::update_counts();
- my %comments = @$comments;
-
- my $problems = Problems::problem_counts();
- my %problems = @$problems;
- %problems = map { $_ => $problems{$_} || 0 } qw(confirmed fixed unconfirmed hidden partial);
- my $total_problems_live = $problems{confirmed} + $problems{fixed};
- my $total_problems = 0;
- map { $total_problems += $_ } values %problems;
-
- my $alerts = Problems::alert_counts($cobrand);
- my %alerts = @$alerts; $alerts{0} ||= 0; $alerts{1} ||= 0;
-
- my $questionnaires = Problems::questionnaire_counts($cobrand);
- my %questionnaires = @$questionnaires;
- $questionnaires{0} ||= 0;
- $questionnaires{1} ||= 0;
- $questionnaires{total} = $questionnaires{0} + $questionnaires{1};
- my $questionnaires_pc = $questionnaires{total} ? sprintf('%.1f', $questionnaires{1} / $questionnaires{total} * 100) : 'na';
-
- print $q->ul(
- $q->li(sprintf(_("<strong>%d</strong> live problems"), $total_problems_live)),
- $q->li(sprintf(_("%d live updates"), $comments{confirmed})),
- $q->li(sprintf(_("%d confirmed alerts, %d unconfirmed"), $alerts{1}, $alerts{0})),
- $q->li(sprintf(_("%d questionnaires sent &ndash; %d answered (%s%%)"), $questionnaires{total}, $questionnaires{1}, $questionnaires_pc)),
- $q->li(sprintf(_("%d council contacts &ndash; %d confirmed, %d unconfirmed"), $contacts{total}, $contacts{1}, $contacts{0})),
- );
-
- if (Cobrand::admin_show_creation_graph($cobrand)) {
- print $q->p( $q->a({ href => mySociety::Config::get('BASE_URL') . "/bci-live-creation.png" },
- _("Graph of problem creation by status over time") ));
-
- }
- print $q->h2(_("Problem breakdown by state"));
- print $q->ul(
- map { $q->li("$problems{$_} $_") } sort keys %problems
- );
-
- print $q->h2(_("Update breakdown by state"));
- print $q->ul(
- map { $q->li("$comments{$_} $_") } sort keys %comments
- );
-
- print html_tail($q);
-}
-
-# admin_councils_list CGI
-sub admin_councils_list ($) {
- my ($q) = @_;
-
- print html_head($q, _("Council contacts"));
- print $q->h1(_("Council contacts"));
-
- # Table of editors
- print $q->h2(_("Diligency prize league table"));
- my $edit_activity = dbh()->selectall_arrayref("select count(*) as c, editor from contacts_history group by editor order by c desc");
- if (@$edit_activity) {
- print $q->ul(
- map { $q->li( sprintf(_('%d edits by %s'), $_->[0], $_->[1])) } @$edit_activity
- );
- } else {
- print $q->p(_('No edits have yet been made.'));
- }
-
- # Table of councils
- print $q->h2(_("Councils"));
- my $cobrand = Page::get_cobrand($q);
- my @area_types = Cobrand::area_types($cobrand);
- my $areas = mySociety::MaPit::call('areas', \@area_types);
- my @councils_ids = sort { strcoll($areas->{$a}->{name}, $areas->{$b}->{name}) } keys %$areas;
- @councils_ids = grep { $_ ne 301 } @councils_ids;
- my $bci_info = dbh()->selectall_hashref("
- select area_id, count(*) as c, count(case when deleted then 1 else null end) as deleted,
- count(case when confirmed then 1 else null end) as confirmed
- from contacts group by area_id", 'area_id');
-
- my $list_part = sub {
- my @ids = @_;
- if (!scalar(@ids)) {
- print _("None");
- return;
- }
- my @li;
- foreach (@ids) {
- my $parent = '';
- $parent = ', ' . $areas->{$areas->{$_}->{parent_area}}->{name}
- if $areas->{$_}->{parent_area};
-
- push @li, $q->li($q->a({ href => NewURL($q, area_id => $_, page => 'councilcontacts') },
- $areas->{$_}->{name}) . $parent . ' ' .
- ($bci_info->{$_} && $q->{site} ne 'emptyhomes' ?
- sprintf(_('%d addresses'), $bci_info->{$_}->{c})
- : ''));
- }
- print $q->ul(@li);
- };
-
- print $q->h3(_('No info at all'));
- &$list_part(grep { !$bci_info->{$_} } @councils_ids);
- print $q->h3(_('Currently has 1+ deleted'));
- &$list_part(grep { $bci_info->{$_} && $bci_info->{$_}->{deleted} } @councils_ids);
- print $q->h3(_('Some unconfirmeds'));
- &$list_part(grep { $bci_info->{$_} && !$bci_info->{$_}->{deleted} && $bci_info->{$_}->{confirmed} != $bci_info->{$_}->{c} } @councils_ids);
- print $q->h3(_('All confirmed'));
- &$list_part(grep { $bci_info->{$_} && !$bci_info->{$_}->{deleted} && $bci_info->{$_}->{confirmed} == $bci_info->{$_}->{c} } @councils_ids);
- print html_tail($q);
-}
-
-# admin_council_contacts CGI AREA_ID
-sub admin_council_contacts ($$) {
- my ($q, $area_id) = @_;
-
- # Submit form
- my $updated = '';
- my $posted = $q->param('posted') || '';
- if ($posted eq 'new') {
- return not_found($q) if $q->param('token') ne get_token($q);
- my $email = trim($q->param('email'));
- my $category = trim($q->param('category'));
- $category = 'Empty property' if $q->{site} eq 'emptyhomes';
- # History is automatically stored by a trigger in the database
- my $update = dbh()->do("update contacts set
- email = ?,
- confirmed = ?,
- deleted = ?,
- editor = ?,
- whenedited = ms_current_timestamp(),
- note = ?
- where area_id = ?
- and category = ?
- ", {},
- $email, ($q->param('confirmed') ? 1 : 0),
- ($q->param('deleted') ? 1 : 0),
- ($q->remote_user() || _("*unknown*")), $q->param('note'),
- $area_id, $category
- );
- $updated = $q->p($q->em(_("Values updated")));
- unless ($update > 0) {
- dbh()->do('insert into contacts
- (area_id, category, email, editor, whenedited, note, confirmed, deleted)
- values
- (?, ?, ?, ?, ms_current_timestamp(), ?, ?, ?)', {},
- $area_id, $category, $email,
- ($q->remote_user() || _('*unknown*')), $q->param('note'),
- ($q->param('confirmed') ? 1 : 0), ($q->param('deleted') ? 1 : 0)
- );
- $updated = $q->p($q->em(_("New category contact added")));
- }
- dbh()->commit();
- } elsif ($posted eq 'update') {
- return not_found($q) if $q->param('token') ne get_token($q);
- my @cats = $q->param('confirmed');
- foreach my $cat (@cats) {
- dbh()->do("update contacts set
- confirmed = 't', editor = ?,
- whenedited = ms_current_timestamp(),
- note = 'Confirmed'
- where area_id = ?
- and category = ?
- ", {},
- ($q->remote_user() || _("*unknown*")),
- $area_id, $cat
- );
- }
- $updated = $q->p($q->em(_("Values updated")));
- dbh()->commit();
- }
-
- my $bci_data = select_all("select * from contacts where area_id = ? order by category", $area_id);
-
- if ($q->param('text')) {
- print $q->header(-type => 'text/plain', -charset => 'utf-8');
- foreach my $l (@$bci_data) {
- next if $l->{deleted} || !$l->{confirmed};
- print $l->{category} . "\t" . $l->{email} . "\n";
- }
- return;
- }
-
- $q->delete_all(); # No need for state!
-
- # Title
- my $mapit_data = mySociety::MaPit::call('area', $area_id);
- my $title = sprintf(_('Council contacts for %s'), $mapit_data->{name});
- print html_head($q, $title);
- print $q->h1($title);
- print $updated;
-
- # Example postcode, link to list of problem reports
- my $links_html;
- my $example_postcode = mySociety::MaPit::call('area/example_postcode', $area_id);
- if ($example_postcode && ! ref $example_postcode) {
- $links_html .= $q->a({ href => mySociety::Config::get('BASE_URL') . '/?pc=' . $q->escape($example_postcode) },
- "Example postcode " . $example_postcode) . " | ";
- }
- $links_html .= ' ' .
- $q->a({ href => mySociety::Config::get('BASE_URL') . "/reports?council=" . $area_id }, _(" List all reported problems"));
- $links_html .= ' ' .
- $q->a({ href => NewURL($q, area_id => $area_id, page => 'councilcontacts', text => 1) }, _('Text only version'));
- print $q->p($links_html);
-
- # Display of addresses / update statuses form
- print $q->start_form(-method => 'POST', -action => './');
- print $q->start_table({border=>1, cellpadding=>2, cellspacing=>0});
- print $q->Tr({}, $q->th({}, [_("Category"), _("Email"), _("Confirmed"), _("Deleted"), _("Last editor"), _("Note"), _("When edited"), _('Confirm')]));
- foreach my $l (@$bci_data) {
- print $q->Tr($q->td([
- $q->a({ href => NewURL($q, area_id => $area_id, category => $l->{category}, page => 'counciledit') },
- $l->{category}), $l->{email}, $l->{confirmed} ? _('Yes') : _('No'),
- $l->{deleted} ? _('Yes') : _('No'), $l->{editor}, ent($l->{note}),
- $l->{whenedited} =~ m/^(.+)\.\d+$/,
- $q->checkbox(-name => 'confirmed', -value => $l->{category}, -label => '')
- ]));
- }
- print $q->end_table();
- # XXX
- print $q->p(
- $q->hidden('area_id', $area_id),
- $q->hidden('posted', 'update'),
- $q->hidden('token', get_token($q)),
- $q->hidden('page', 'councilcontacts'),
- $q->submit(_('Update statuses'))
- );
- print $q->end_form();
-
- # Display form for adding new category
- print $q->h2(_('Add new category'));
- print $q->start_form(-method => 'POST', -action => './');
- if ($q->{site} ne 'emptyhomes') {
- print $q->p($q->strong(_("Category: ")),
- $q->textfield(-name => "category", -size => 30));
- }
- print $q->p($q->strong(_("Email: ")),
- $q->textfield(-name => "email", -size => 30));
- $q->autoEscape(0);
- print $q->p(
- $q->checkbox(-id => 'confirmed', -name => "confirmed", -value => 1, -label => ' ' . $q->label({-for => 'confirmed'}, _('Confirmed'))),
- ' ',
- $q->checkbox(-id => 'deleted', -name => "deleted", -value => 1, -label => ' ' . $q->label({-for => 'deleted'}, _('Deleted')))
- );
- $q->autoEscape(1);
- print $q->p($q->strong(_("Note: ")),
- $q->textarea(-name => "note", -rows => 3, -columns=>40));
- print $q->p(
- $q->hidden('area_id', $area_id),
- $q->hidden('posted', 'new'),
- $q->hidden('token', get_token($q)),
- $q->hidden('page', 'councilcontacts'),
- $q->submit(_('Create category'))
- );
- print $q->end_form();
-
- print html_tail($q);
-}
-
-# admin_council_edit CGI AREA_ID CATEGORY
-sub admin_council_edit ($$$) {
- my ($q, $area_id, $category) = @_;
-
- # Get all the data
- my $bci_data = select_all("select * from contacts where area_id = ? and category = ?", $area_id, $category);
- $bci_data = $bci_data->[0];
- my $bci_history = select_all("select * from contacts_history where area_id = ? and category = ? order by contacts_history_id", $area_id, $category);
- my $mapit_data = mySociety::MaPit::call('area', $area_id);
-
- # Title
- my $title = sprintf(_('Council contacts for %s'), $mapit_data->{name});
- print html_head($q, $title);
- print $q->h1($title);
-
- # Example postcode
- my $example_postcode = mySociety::MaPit::call('area/example_postcode', $area_id);
- if ($example_postcode && ! ref $example_postcode) {
- print $q->p("Example postcode: ",
- $q->a({ href => mySociety::Config::get('BASE_URL') . '/?pc=' . $q->escape($example_postcode) },
- $example_postcode));
- }
-
- # Display form for editing details
- print $q->start_form(-method => 'POST', -action => './');
- map { $q->param($_, $bci_data->{$_}) } qw/category email confirmed deleted/;
- $q->param('page', 'councilcontacts');
- $q->param('posted', 'new');
- print $q->strong(_("Category: ")) . $bci_data->{category};
- print $q->hidden('token', get_token($q)),
- print $q->hidden("category");
- print $q->strong(' ' . _("Email: "));
- print $q->textfield(-name => "email", -size => 30) . " ";
- $q->autoEscape(0);
- print $q->checkbox(-id => 'confirmed', -name => "confirmed", -value => 1, -label => ' ' . $q->label({-for => 'confirmed'}, _('Confirmed')));
- print ' ';
- print $q->checkbox(-id => 'deleted', -name => "deleted", -value => 1, -label => ' ' . $q->label({-for => 'deleted'}, _('Deleted')));
- $q->autoEscape(1);
- print $q->br();
- print $q->strong(_("Note: "));
- print $q->textarea(-name => "note", -rows => 3, -columns=>40) . " ";
- print $q->br();
- print $q->hidden('area_id');
- print $q->hidden('posted');
- print $q->hidden('page');
- print $q->submit(_('Save changes'));
- print $q->end_form();
-
- # Display history of changes
- print $q->h2(_('History'));
- print $q->start_table({border=>1});
- print $q->Tr({}, $q->th({}, [_("When edited"), _("Email"), _("Confirmed"), _("Deleted"), _("Editor"), _("Note")]));
- my $html = '';
- my $prev = undef;
- foreach my $h (@$bci_history) {
- $h->{confirmed} = $h->{confirmed} ? _("yes") : _("no"),
- $h->{deleted} = $h->{deleted} ? _("yes") : _("no"),
- my $emailchanged = ($prev && $h->{email} ne $prev->{email}) ? 1 : 0;
- my $confirmedchanged = ($prev && $h->{confirmed} ne $prev->{confirmed}) ? 1 : 0;
- my $deletedchanged = ($prev && $h->{deleted} ne $prev->{deleted}) ? 1 : 0;
- $html .= $q->Tr({}, $q->td([
- $h->{whenedited} =~ m/^(.+)\.\d+$/,
- $emailchanged ? $q->strong($h->{email}) : $h->{email},
- $confirmedchanged ? $q->strong($h->{confirmed}) : $h->{confirmed},
- $deletedchanged ? $q->strong($h->{deleted}) : $h->{deleted},
- $h->{editor},
- $h->{note}
- ]));
- $prev = $h;
- }
- print $html;
- print $q->end_table();
- print html_tail($q);
-}
-
-sub admin_reports {
- my $q = shift;
- my $title = _('Search Reports');
- my $cobrand = Page::get_cobrand($q);
- my $pages = allowed_pages($q);
- print html_head($q, $title);
- print $q->h1($title);
- print $q->start_form(-method => 'GET', -action => './');
- print $q->label({-for => 'search'}, _('Search:')), ' ', $q->textfield(-id => 'search', -name => "search", -size => 30);
- print $q->hidden('page');
- print $q->end_form;
-
- if (my $search = $q->param('search')) {
- my $results = Problems::problem_search($search);
- print $q->start_table({border=>1, cellpadding=>2, cellspacing=>0});
- print $q->Tr({}, $q->th({}, [_('ID'), _('Title'), _('Name'), _('Email'), _('Council'), _('Category'), _('Anonymous'), _('Cobrand'), _('Created'), _('State'), _('When sent'), _('*') ]));
- my $cobrand_data;
- foreach (@$results) {
- my $url = $_->{id};
- if ($_->{state} eq 'confirmed' || $_->{state} eq 'fixed') {
- # if this is a cobranded admin interface, but we're looking at a generic problem, figure out enough information
- # to create a URL to the cobranded version of the problem
- if ($_->{cobrand}) {
- $cobrand_data = $_->{cobrand_data};
- } else {
- $cobrand_data = Cobrand::cobrand_data_for_generic_problem($cobrand, $_);
- }
- $url = $q->a({ -href => Cobrand::base_url_for_emails($cobrand, $cobrand_data) . '/report/' . $_->{id} }, $url);
- }
- my $council = $_->{council} || '&nbsp;';
- my $category = $_->{category} || '&nbsp;';
- (my $confirmed = $_->{confirmed} || '-') =~ s/ (.*?)\..*/&nbsp;$1/;
- (my $created = $_->{created}) =~ s/\..*//;
- (my $lastupdate = $_->{lastupdate}) =~ s/ (.*?)\..*/&nbsp;$1/;
- (my $whensent = $_->{whensent} || '&nbsp;') =~ s/\..*//;
- my $state = $_->{state};
- $state .= '<small>';
- $state .= "<br>" . _('Confirmed:') . "&nbsp;$confirmed" if $_->{state} eq 'confirmed' || $_->{state} eq 'fixed';
- $state .= '<br>' . _('Fixed:') . ' ' . $lastupdate if $_->{state} eq 'fixed';
- $state .= "<br>" . _('Last&nbsp;update:') . "&nbsp;$lastupdate" if $_->{state} eq 'confirmed';
- $state .= '</small>';
- my $anonymous = $_->{anonymous} ? _('Yes') : _('No');
- my $cobrand = $_->{cobrand};
- $cobrand .= "<br>" . $_->{cobrand_data};
- my $counciltext = '';
- if (grep {$_ eq 'councilcontacts'} keys %{$pages}) {
- $counciltext = $q->a({ -href => NewURL($q, page=>'councilcontacts', area_id=>$council)}, $council);
- } else {
- $counciltext = $council;
- }
- my $attr = {};
- $attr->{-class} = 'hidden' if $_->{state} eq 'hidden';
- print $q->Tr($attr, $q->td([ $url, ent($_->{title}), ent($_->{name}), ent($_->{email}),
- $counciltext,
- $category, $anonymous, $cobrand, $created, $state, $whensent,
- $q->a({ -href => NewURL($q, page=>'report_edit', id=>$_->{id}) }, _('Edit'))
- ]));
- }
- print $q->end_table;
-
- print $q->h2(_('Updates'));
- my $updates = Problems::update_search($search);
- admin_show_updates($q, $updates);
- }
-
- print html_tail($q);
-}
-
-sub admin_edit_report {
- my ($q, $id) = @_;
- my $row = Problems::admin_fetch_problem($id);
- my $cobrand = Page::get_cobrand($q);
- return not_found($q) if ! $row->[0];
- my %row = %{$row->[0]};
- my $status_message = '';
- if ($q->param('resend')) {
- return not_found($q) if $q->param('token') ne get_token($q);
- dbh()->do('update problem set whensent=null where id=?', {}, $id);
- admin_log_edit($q, $id, 'problem', 'resend');
- dbh()->commit();
- $status_message = '<p><em>' . _('That problem will now be resent.') . '</em></p>';
- } elsif ($q->param('submit')) {
- return not_found($q) if $q->param('token') ne get_token($q);
- my $new_state = $q->param('state');
- my $done = 0;
- if ($new_state eq 'confirmed' && $row{state} eq 'unconfirmed' && $q->{site} eq 'emptyhomes') {
- $status_message = '<p><em>' . _('I am afraid you cannot confirm unconfirmed reports.') . '</em></p>';
- $done = 1;
- }
- my $query = 'update problem set anonymous=?, state=?, name=?, email=?, title=?, detail=?';
- if ($q->param('remove_photo')) {
- $query .= ', photo=null';
- }
- if ($new_state ne $row{state}) {
- $query .= ', lastupdate=current_timestamp';
- }
- if ($new_state eq 'confirmed' and $row{state} eq 'unconfirmed') {
- $query .= ', confirmed=current_timestamp';
- }
- $query .= ' where id=?';
- unless ($done) {
- dbh()->do($query, {}, $q->param('anonymous') ? 't' : 'f', $new_state,
- $q->param('name'), $q->param('email'), $q->param('title'), $q->param('detail'), $id);
- if ($new_state ne $row{state}) {
- admin_log_edit($q, $id, 'problem', 'state_change');
- }
- if ($q->param('anonymous') ne $row{anonymous} ||
- $q->param('name') ne $row{name} ||
- $q->param('email') ne $row{email} ||
- $q->param('title') ne $row{title} ||
- $q->param('detail') ne $row{detail}) {
- admin_log_edit($q, $id, 'problem', 'edit');
- }
- dbh()->commit();
- map { $row{$_} = $q->param($_) } qw(anonymous state name email title detail);
- $status_message = '<p><em>' . _('Updated!') . '</em></p>';
- }
- }
- my %row_h = map { $_ => $row{$_} ? ent($row{$_}) : '' } keys %row;
- my $title = sprintf(_("Editing problem %d"), $id);
- print html_head($q, $title);
- print $q->h1($title);
- print $status_message;
-
- my $council = $row{council} || '<em>' . _('None') . '</em>';
- (my $areas = $row{areas}) =~ s/^,(.*),$/$1/;
- my $latitude = $row{latitude};
- my $longitude = $row{longitude};
- my $questionnaire = $row{send_questionnaire} ? _('Yes') : _('No');
- my $used_map = $row{used_map} ? _('used map') : _("didn't use map");
- (my $whensent = $row{whensent} || '&nbsp;') =~ s/\..*//;
- (my $confirmed = $row{confirmed} || '-') =~ s/ (.*?)\..*/&nbsp;$1/;
- my $photo = '';
- my $cobrand_data;
- if ($row{cobrand}) {
- $cobrand_data = $row{cobrand_data};
- } else {
- $cobrand_data = Cobrand::cobrand_data_for_generic_problem($cobrand, \%row);
- }
- $photo = '<li><img align="top" src="' . Cobrand::base_url_for_emails($cobrand, $cobrand_data) . '/photo?id=' . $row{id} . '">
-<input type="checkbox" id="remove_photo" name="remove_photo" value="1">
-<label for="remove_photo">' . _("Remove photo (can't be undone!)") . '</label>' if $row{photo};
-
- my $url_base = Cobrand::base_url_for_emails($cobrand, $cobrand_data);
- my $url = $url_base . '/report/' . $row{id};
-
- my $anon = $q->label({-for=>'anonymous'}, _('Anonymous:')) . ' ' . $q->popup_menu(-id => 'anonymous', -name => 'anonymous', -values => { 1=>_('Yes'), 0=>_('No') }, -default => $row{anonymous});
- my $state = $q->label({-for=>'state'}, _('State:')) . ' ' . $q->popup_menu(-id => 'state', -name => 'state', -values => { confirmed => _('Open'), fixed => _('Fixed'), hidden => _('Hidden'), unconfirmed => _('Unconfirmed'), partial => _('Partial') }, -default => $row{state});
-
- my $resend = '';
- $resend = ' <input onclick="return confirm(\'' . _('You really want to resend?') . '\')" type="submit" name="resend" value="' . _('Resend report') . '">' if $row{state} eq 'confirmed';
-
- print $q->start_form(-method => 'POST', -action => './');
- print $q->hidden('page');
- print $q->hidden('id');
- print $q->hidden('token', get_token($q));
- print $q->hidden('submit', 1);
- print "
-<ul>
-<li><a href='$url'>" . _('View report on site') . "</a>
-<li><label for='title'>" . _('Subject:') . "</label> <input size=60 type='text' id='title' name='title' value='$row_h{title}'>
-<li><label for='detail'>" . _('Details:') . "</label><br><textarea name='detail' id='detail' cols=60 rows=10>$row_h{detail}</textarea>
-<li>" . _('Co-ordinates:') . " $latitude,$longitude (" . _('originally entered') . " $row_h{postcode}, $used_map)
-<li>" . _('For council(s):') . " $council (" . _('other areas:') . " $areas)
-<li>$anon
-<li>$state
-<li>" . _('Category:') . " $row{category}
-<li>" . _('Name:') . " <input type='text' name='name' id='name' value='$row_h{name}'>
-<li>" . _('Email:') . " <input type='text' id='email' name='email' value='$row_h{email}'>
-<li>" . _('Phone:') . " $row_h{phone}
-<li>" . _('Created:') . " $row{created}
-<li>" . _('Confirmed:') . " $confirmed
-<li>" . _('Sent:') . " $whensent $resend
-<li>" . _('Last update:') . " $row{lastupdate}
-<li>" . _('Service:') . " $row{service}
-<li>" . _('Cobrand:') . " $row{cobrand}
-<li>" . _('Cobrand data:') . " $row{cobrand_data}
-<li>" . _('Going to send questionnaire?') . " $questionnaire
-$photo
-</ul>
-";
- print $q->submit(_('Submit changes'));
- print $q->end_form;
-
- print $q->h2(_('Updates'));
- my $updates = select_all('select * from comment where problem_id=? order by created', $id);
- admin_show_updates($q, $updates);
- print html_tail($q);
-}
-
-sub admin_show_updates {
- my ($q, $updates) = @_;
- my $cobrand = Page::get_cobrand($q);
- print $q->start_table({border=>1, cellpadding=>2, cellspacing=>0});
- print $q->Tr({}, $q->th({}, [ _('ID'), _('State'), _('Name'), _('Email'), _('Created'), _('Cobrand'), _('Text'), _('*') ]));
- my $base_url = '';
- my $cobrand_data;
- foreach (@$updates) {
- my $url = $_->{id};
- if ( $_->{state} eq 'confirmed' ) {
- if ($_->{cobrand}) {
- $cobrand_data = $_->{cobrand_data};
- } else {
- $cobrand_data = Cobrand::cobrand_data_for_generic_update($cobrand, $_);
- }
- $url = $q->a({ -href => Cobrand::base_url_for_emails($cobrand, $cobrand_data) . '/report/' . $_->{problem_id} . '#update_' . $_->{id} },
- $url);
- }
- my $cobrand = $_->{cobrand} . '<br>' . $_->{cobrand_data};
- my $attr = {};
- $attr->{-class} = 'hidden' if $_->{state} eq 'hidden' || ($_->{problem_state} && $_->{problem_state} eq 'hidden');
- print $q->Tr($attr, $q->td([ $url, $_->{state}, ent($_->{name} || ''),
- ent($_->{email}), $_->{created}, $cobrand, ent($_->{text}),
- $q->a({ -href => NewURL($q, page=>'update_edit', id=>$_->{id}) }, _('Edit'))
- ]));
- }
- print $q->end_table;
-}
-
-sub admin_edit_update {
- my ($q, $id) = @_;
- my $row = Problems::admin_fetch_update($id);
- return not_found($q) if ! $row->[0];
- my $cobrand = Page::get_cobrand($q);
-
- my %row = %{$row->[0]};
- my $status_message = '';
- if ($q->param('submit')) {
- return not_found($q) if $q->param('token') ne get_token($q);
- my $query = 'update comment set state=?, name=?, email=?, text=?';
- if ($q->param('remove_photo')) {
- $query .= ', photo=null';
- }
- $query .= ' where id=?';
- dbh()->do($query, {}, $q->param('state'), $q->param('name'), $q->param('email'), $q->param('text'), $id);
- $status_message = '<p><em>' . _('Updated!') . '</em></p>';
-
- # If we're hiding an update, see if it marked as fixed and unfix if so
- if ($q->param('state') eq 'hidden' && $row{mark_fixed}) {
- dbh()->do("update problem set state='confirmed' where state='fixed' and id=?", {}, $row{problem_id});
- $status_message .= '<p><em>' . _('Problem marked as open.') . '</em></p>';
- }
-
- if ($q->param('state') ne $row{state}) {
- admin_log_edit($q, $id, 'update', 'state_change');
- }
- if (!defined($row{name})){
- $row{name} = "";
- }
- if ($q->param('name') ne $row{name} || $q->param('email') ne $row{email} || $q->param('text') ne $row{text}) {
- admin_log_edit($q, $id, 'update', 'edit');
- }
- dbh()->commit();
- map { $row{$_} = $q->param($_) } qw(state name email text);
- }
- my %row_h = map { $_ => $row{$_} ? ent($row{$_}) : '' } keys %row;
- my $title = sprintf(_("Editing update %d"), $id);
- print html_head($q, $title);
- print $q->h1($title);
- print $status_message;
- my $name = $row_h{name};
- $name = '' unless $name;
- my $cobrand_data;
- if ($row{cobrand}) {
- $cobrand_data = $row{cobrand_data};
- } else {
- $cobrand_data = Cobrand::cobrand_data_for_generic_update($cobrand, \%row);
- }
- my $photo = '';
- $photo = '<li><img align="top" src="' . Cobrand::base_url_for_emails($cobrand, $cobrand_data) . '/photo?c=' . $row{id} . '">
-<input type="checkbox" id="remove_photo" name="remove_photo" value="1">
-<label for="remove_photo">' . _("Remove photo (can't be undone!)") . '</label>' if $row{photo};
-
- my $url = Cobrand::base_url_for_emails($cobrand, $cobrand_data) . '/report/' . $row{problem_id} . '#update_' . $row{id};
-
- my $state = $q->label({-for=>'state'}, _('State:')) . ' ' . $q->popup_menu(-id => 'state', -name => 'state', -values => { confirmed => _('Confirmed'), hidden => _('Hidden'), unconfirmed => _('Unconfirmed') }, -default => $row{state});
-
- print $q->start_form(-method => 'POST', -action => './');
- print $q->hidden('page');
- print $q->hidden('id');
- print $q->hidden('token', get_token($q));
- print $q->hidden('submit', 1);
- print "
-<ul>
-<li><a href='$url'>" . _('View update on site') . "</a>
-<li><label for='text'>" . _('Text:') . "</label><br><textarea name='text' id='text' cols=60 rows=10>$row_h{text}</textarea>
-<li>$state
-<li>" . _('Name:') . " <input type='text' name='name' id='name' value='$name'> " . _('(blank to go anonymous)') . "
-<li>" . _('Email:') . " <input type='text' id='email' name='email' value='$row_h{email}'>
-<li>" . _('Cobrand:') . " $row{cobrand}
-<li>" . _('Cobrand data:') . " $row{cobrand_data}
-<li>" . _('Created:') . " $row{created}
-$photo
-</ul>
-";
- print $q->submit(_('Submit changes'));
- print $q->end_form;
- print html_tail($q);
-}
-
-sub get_cobrand_data_from_hash {
- my ($cobrand, $data) = @_;
- my $cobrand_data;
- if ($data->{cobrand}) {
- $cobrand_data = $data->{cobrand_data};
- } else {
- $cobrand_data = Cobrand::cobrand_data_for_generic_problem($cobrand, $data);
- }
- return $cobrand_data;
-}
-
-sub admin_log_edit {
- my ($q, $id, $object_type, $action) = @_;
- my $query = "insert into admin_log (admin_user, object_type, object_id, action)
- values (?, ?, ?, ?);";
- dbh()->do($query, {}, $q->remote_user(), $object_type, $id, $action);
-}
-
-sub admin_timeline {
- my $q = shift;
- my $cobrand = Page::get_cobrand($q);
- print html_head($q, _('Timeline'));
- print $q->h1(_('Timeline'));
-
- my %time;
- #my $backto_unix = time() - 60*60*24*7;
-
- my $probs = Problems::timeline_problems();
- foreach (@$probs) {
- push @{$time{$_->{created}}}, { type => 'problemCreated', %$_ };
- push @{$time{$_->{confirmed}}}, { type => 'problemConfirmed', %$_ } if $_->{confirmed};
- push @{$time{$_->{whensent}}}, { type => 'problemSent', %$_ } if $_->{whensent};
- }
-
- my $questionnaire = Problems::timeline_questionnaires($cobrand);
- foreach (@$questionnaire) {
- push @{$time{$_->{whensent}}}, { type => 'quesSent', %$_ };
- push @{$time{$_->{whenanswered}}}, { type => 'quesAnswered', %$_ } if $_->{whenanswered};
- }
-
- my $updates = Problems::timeline_updates();
- foreach (@$updates) {
- push @{$time{$_->{created}}}, { type => 'update', %$_} ;
- }
-
- my $alerts = Problems::timeline_alerts($cobrand);
-
-
- foreach (@$alerts) {
- push @{$time{$_->{whensubscribed}}}, { type => 'alertSub', %$_ };
- }
- $alerts = Problems::timeline_deleted_alerts($cobrand);
- foreach (@$alerts) {
- push @{$time{$_->{whendisabled}}}, { type => 'alertDel', %$_ };
- }
-
- my $date = '';
- my $cobrand_data;
- foreach (reverse sort keys %time) {
- my $curdate = decode_utf8(strftime('%A, %e %B %Y', localtime($_)));
- if ($date ne $curdate) {
- print '</dl>' if $date;
- print "<h2>$curdate</h2> <dl>";
- $date = $curdate;
- }
- print '<dt><b>', decode_utf8(strftime('%H:%M:%S', localtime($_))), ':</b></dt> <dd>';
- foreach (@{$time{$_}}) {
- my $type = $_->{type};
- if ($type eq 'problemCreated') {
- my $name_str = '; ' . sprintf(_("by %s"), ent($_->{name})) . " &lt;" . ent($_->{email}) . "&gt;, '" . ent($_->{title}) . "'";
- print sprintf(_("Problem %d created"), $_->{id}) . $name_str;
- } elsif ($type eq 'problemConfirmed') {
- my $name_str = '; ' . sprintf(_("by %s"), ent($_->{name})) . " &lt;" . ent($_->{email}) . "&gt;, '" . ent($_->{title}) . "'";
- $cobrand_data = get_cobrand_data_from_hash($cobrand, $_);
- my $url = Cobrand::base_url_for_emails($cobrand, $cobrand_data) . "/report/$_->{id}";
- print sprintf(_("Problem %s confirmed"), "<a href='$url'>$_->{id}</a>") . $name_str;
- } elsif ($type eq 'problemSent') {
- $cobrand_data = get_cobrand_data_from_hash($cobrand, $_);
- my $url = Cobrand::base_url_for_emails($cobrand, $cobrand_data) . "/report/$_->{id}";
- print sprintf(_("Problem %s sent to council %s"), "<a href='$url'>$_->{id}</a>", $_->{council});
- } elsif ($type eq 'quesSent') {
- print sprintf(_("Questionnaire %d sent for problem %d"), $_->{id}, $_->{problem_id});
- } elsif ($type eq 'quesAnswered') {
- print sprintf(_("Questionnaire %d answered for problem %d, %s to %s"), $_->{id}, $_->{problem_id}, $_->{old_state}, $_->{new_state});
- } elsif ($type eq 'update') {
- $cobrand_data = get_cobrand_data_from_hash($cobrand, $_);
- my $url = Cobrand::base_url_for_emails($cobrand, $cobrand_data) . "/report/$_->{problem_id}#$_->{id}";
- my $name = ent($_->{name} || 'anonymous');
- print sprintf(_("Update %s created for problem %d; by %s"), "<a href='$url'>$_->{id}</a>", $_->{problem_id}, $name) . " &lt;" . ent($_->{email}) . "&gt;";
- } elsif ($type eq 'alertSub') {
- my $param = $_->{parameter} || '';
- my $param2 = $_->{parameter2} || '';
- print sprintf(_("Alert %d created for %s, type %s, parameters %s / %s"), $_->{id}, ent($_->{email}), $_->{alert_type}, $param, $param2);
- } elsif ($type eq 'alertDel') {
- my $sub = decode_utf8(strftime('%H:%M:%S %e %B %Y', localtime($_->{whensubscribed})));
- print sprintf(_("Alert %d disabled (created %s)"), $_->{id}, $sub);
- }
- print '<br>';
- }
- print "</dd>\n";
- }
- print html_tail($q);
-
-}
-
-sub admin_questionnaire {
- my $q = shift;
- my $cobrand = Page::get_cobrand($q);
- print html_head($q, _('Survey Results'));
- print $q->h1(_('Survey Results'));
-
- # columns in questionnaire is id, problem_id, whensent,
- # whenanswered, ever_reported, old_state, new_state
-
- my $survey = select_all("select ever_reported, count(*) from questionnaire where whenanswered is not null group by ever_reported");
-
- my %res;
- $res{0} = 0;
- $res{1} = 0;
- foreach my $h (@$survey) {
- $res{$h->{ever_reported}} = $h->{count} if (exists $h->{ever_reported});
- }
- my $total = $res{0} + $res{1};
-
- print $q->start_table({border=>1});
- print $q->Tr({},
- $q->th({}, [_("Reported before"),
- _("Not reported before")]));
- if ($total) {
- print $q->Tr({},
- $q->td([
- sprintf("%d (%d%%)", $res{1}, (100 * $res{1}) / $total),
- sprintf("%d (%d%%)", $res{0}, (100 * $res{0}) / $total),
- ]));
- } else {
- print $q->Tr({}, $q->td([ 'n/a', 'n/a' ]));
- }
- print $q->end_table();
- print html_tail($q);
-}
-
-sub not_found {
- my ($q) = @_;
- print $q->header(-status=>'404 Not Found',-type=>'text/html');
- print "<h1>Not Found</h1>The requested URL was not found on this server.";
-}
-
-sub main {
- my $q = shift;
-
- my $logout = $q->param('logout');
- my $timeout = $q->param('timeout');
- if ($logout) {
- if (!$timeout) {
- print $q->redirect(-location => '?logout=1;timeout=' . (time() + 7));
- return;
- }
- if (time() < $timeout) {
- print $q->header(
- -status => '401 Unauthorized',
- -www_authenticate => 'Basic realm="www.fixmystreet.com admin pages"'
- );
- return;
- }
- }
-
- my $page = $q->param('page');
- $page = "summary" if !$page;
-
- my $area_id = $q->param('area_id');
- my $category = $q->param('category');
- my $pages = allowed_pages($q);
- my @allowed_actions = keys %$pages;
-
- if (!grep {$_ eq $page} @allowed_actions) {
- not_found($q);
- return;
- }
-
- if ($page eq "councilslist") {
- admin_councils_list($q);
- } elsif ($page eq "councilcontacts") {
- admin_council_contacts($q, $area_id);
- } elsif ($page eq "counciledit") {
- admin_council_edit($q, $area_id, $category);
- } elsif ($page eq 'reports') {
- admin_reports($q);
- } elsif ($page eq 'report_edit') {
- my $id = $q->param('id');
- admin_edit_report($q, $id);
- } elsif ($page eq 'update_edit') {
- my $id = $q->param('id');
- admin_edit_update($q, $id);
- } elsif ($page eq 'timeline') {
- admin_timeline($q);
- } elsif ($page eq 'questionnaire') {
- admin_questionnaire($q);
- } else {
- admin_summary($q);
- }
-}
-Page::do_fastcgi(\&main);
-
-sub trim {
- my $e = shift;
- $e =~ s/^\s+//;
- $e =~ s/\s+$//;
- return $e;
-}
diff --git a/web/about.cgi b/web/about.cgi
deleted file mode 100755
index 6b0347ecf..000000000
--- a/web/about.cgi
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# about.cgi:
-# For EHA
-#
-# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: about.cgi,v 1.10 2009-08-03 10:45:28 matthew Exp $
-
-use strict;
-use Standard -db;
-
-my $lastmodified = (stat $0)[9];
-
-# Main code for index.cgi
-sub main {
- my $q = shift;
- print Page::header($q, title=>_('About us'));
- if ($q->{site} eq 'emptyhomes') {
- print $q->h1(_('About us'));
- print '<div style="float: left; width: 48%;">';
- print _(<<ABOUTUS);
-<h2>The Empty Homes Agency</h2>
-<p>The Empty Homes agency is an independent campaigning charity. We are not
-part of government, and have no formal links with local councils although we
-work in cooperation with both. We exist to highlight the waste of empty
-property and work with others to devise and promote sustainable solutions to
-bring empty property back into use. We are based in London but work across
-England. We also work in partnership with other charities across the UK.</p>
-ABOUTUS
- print '</div> <div style="float: right; width:48%;">';
- print _(<<ABOUTUS);
-<h2>Shelter Cymru</h2>
-Shelter Cymru is Wales&rsquo; people and homes charity and wants everyone in Wales to
-have a decent home. We believe a home is a fundamental right and essential to
-the health and well-being of people and communities. We work for people in
-housing need. We have offices all over Wales and prevent people from losing
-their homes by offering free, confidential and independent advice. When
-necessary we constructively challenge on behalf of people to ensure they are
-properly assisted and to improve practice and learning. We believe that
-bringing empty homes back into use can make a significant contribution to the
-supply of affordable homes in Wales.
-<a href="http://www.sheltercymru.org.uk/shelter/advice/pdetail.asp?cat=20">Further information about our work on
-empty homes</a>.
-ABOUTUS
- print '</div>';
- }
- print Page::footer($q);
-}
-Page::do_fastcgi(\&main, $lastmodified);
-
diff --git a/web/ajax.cgi b/web/ajax.cgi
deleted file mode 100755
index f13529852..000000000
--- a/web/ajax.cgi
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# ajax.cgi:
-# Updating the pins as you drag the map
-#
-# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: ajax.cgi,v 1.19 2009-12-15 17:53:52 louise Exp $
-
-use strict;
-use Standard;
-use mySociety::Web qw(ent NewURL);
-
-sub main {
- my $q = shift;
-
- my @vars = qw(x y sx sy all_pins);
- my %input = map { $_ => $q->param($_) || '' } @vars;
- my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
-
- # Our current X/Y middle of visible map
- my $x = $input{x};
- my $y = $input{y};
- $x ||= 0; $x += 0;
- $y ||= 0; $y += 0;
-
- # Where we started as that's the (0,0) we have to work to
- my $sx = $input{sx};
- my $sy = $input{sy};
- $sx ||= 0; $sx += 0;
- $sy ||= 0; $sy += 0;
-
- my $interval;
- unless ($input{all_pins}) {
- $interval = '6 months';
- }
- my ($pins, $on_map, $around_map, $dist) = FixMyStreet::Map::map_pins($q, $x, $y, $sx, $sy, $interval);
- my $cobrand = Page::get_cobrand($q);
- my $list = '';
- my $link = '';
- foreach (@$on_map) {
- $link = Cobrand::url($cobrand, NewURL($q, -retain => 1,
- -url => '/report/' . $_->{id},
- pc => undef,
- x => undef,
- y => undef,
- sx => undef,
- sy => undef,
- all_pins => undef,
- no_pins => undef), $q);
- $list .= '<li><a href="' . $link . '">';
- $list .= ent($_->{title}) . '</a> <small>(';
- $list .= Page::prettify_epoch($q, $_->{time}, 1) . ')</small>';
- $list .= ' <small>' . _('(fixed)') . '</small>' if $_->{state} eq 'fixed';
- $list .= '</li>';
- }
- my $om_list = $list;
-
- $list = '';
- foreach (@$around_map) {
- my $dist = int($_->{distance}*10+.5)/10;
- $link = Cobrand::url($cobrand, NewURL($q, -retain => 1,
- -url => '/report/' . $_->{id},
- pc => undef,
- x => undef,
- y => undef,
- sx => undef,
- sy => undef,
- all_pins => undef,
- no_pins => undef), $q);
- $list .= '<li><a href="' . $link . '">';
- $list .= ent($_->{title}) . '</a> <small>(';
- $list .= Page::prettify_epoch($q, $_->{time}, 1) . ', ';
- $list .= $dist . 'km)</small>';
- $list .= ' <small>' . _('(fixed)') . '</small>' if $_->{state} eq 'fixed';
- $list .= '</li>';
- }
- my $am_list = $list;
-
- #$list = '';
- #foreach (@$fixed) {
- # $list .= '<li><a href="/report/' . $_->{id} . '">';
- # $list .= $_->{title} . ' <small>(' . int($_->{distance}*10+.5)/10 . 'km)</small>';
- # $list .= '</a></li>';
- #}
- #my $f_list = $list;
-
- # For now, assume this is not cacheable - may need to be more fine-grained later
- print $q->header(-charset => 'utf-8', -content_type => 'text/javascript', -Cache_Control => 'max-age=0');
-
-
- $pins =~ s/'/\\'/g;
- $om_list =~ s/'/\\'/g;
- $am_list =~ s/'/\\'/g;
- #$f_list =~ s/'/\\'/g;
- print <<EOF;
-({
-'pins': '$pins',
-'current': '$om_list',
-'current_near': '$am_list'
-})
-EOF
-}
-
-Page::do_fastcgi(\&main);
-
diff --git a/web/alert.cgi b/web/alert.cgi
deleted file mode 100755
index 208dc756b..000000000
--- a/web/alert.cgi
+++ /dev/null
@@ -1,610 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# alert.cgi:
-# Alert code for FixMyStreet
-#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: alert.cgi,v 1.67 2010-01-06 10:08:22 louise Exp $
-
-use strict;
-use Standard;
-use Digest::SHA1 qw(sha1_hex);
-use Encode;
-use Error qw(:try);
-use CrossSell;
-use FixMyStreet::Alert;
-use FixMyStreet::Geocode;
-use mySociety::AuthToken;
-use mySociety::Config;
-use mySociety::DBHandle qw(select_all);
-use mySociety::EmailUtil qw(is_valid_email);
-use mySociety::Gaze;
-use mySociety::MaPit;
-use mySociety::VotingArea;
-use mySociety::Web qw(ent);
-use Cobrand;
-use Utils;
-
-sub main {
- my $q = shift;
- my $out = '';
- my $title = _('Confirmation');
- if ($q->param('signed_email')) {
- $out = alert_signed_input($q);
- } elsif (my $token = $q->param('token')) {
- my $data = mySociety::AuthToken::retrieve('alert', $token);
- if ($data->{id}) {
- $out = alert_token($q, $data);
- } else {
- my $contact_url = Cobrand::url(Page::get_cobrand($q), '/contact', $q);
- $out = $q->p(sprintf(_(<<EOF), $contact_url));
-Thank you for trying to confirm your alert. We seem to have an error ourselves
-though, so <a href="%s">please let us know what went on</a> and we'll look into it.
-EOF
- my %vars = (error => $out);
- my $cobrand_page = Page::template_include('error', $q, Page::template_root($q), %vars);
- $out = $cobrand_page if $cobrand_page;
-
- }
- } elsif ($q->param('rss')) {
- $out = alert_rss($q);
- return unless $out;
- } elsif ($q->param('rznvy')) {
- $out = alert_do_subscribe($q, $q->param('rznvy'));
- } elsif ($q->param('id')) {
- $out = alert_updates_form($q);
- } elsif ($q->param('type') && $q->param('feed')) {
- $title = _('Local RSS feeds and email alerts');
- $out = alert_local_form($q);
- } elsif ($q->param('pc') || ($q->param('lat') || $q->param('lon'))) {
- $title = _('Local RSS feeds and email alerts');
- $out = alert_list($q);
- } else {
- $title = _('Local RSS feeds and email alerts');
- $out = alert_front_page($q);
- }
-
- print Page::header($q, title => $title, robots => 'noindex,nofollow');
- print $out;
- print Page::footer($q);
-}
-Page::do_fastcgi(\&main);
-
-sub alert_list {
- my ($q, @errors) = @_;
- my @vars = qw(pc rznvy lat lon);
- my %input = map { $_ => scalar $q->param($_) } @vars;
- my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
-
- my($error, $lat, $lon);
- if ($input{lat} || $input{lon}) {
- $lat = $input{lat};
- $lon = $input{lon};
- } else {
- try {
- ($lat, $lon, $error) = FixMyStreet::Geocode::lookup($input{pc}, $q);
- } catch Error::Simple with {
- $error = shift;
- };
- }
-
- return FixMyStreet::Geocode::list_choices($error, '/alert', $q) if ref($error) eq 'ARRAY';
- return alert_front_page($q, $error) if $error;
-
- my $pretty_pc = $input_h{pc};
- my $pretty_pc_text;# This one isnt't getting the nbsp.
- if (mySociety::PostcodeUtil::is_valid_postcode($input{pc})) {
- $pretty_pc = mySociety::PostcodeUtil::canonicalise_postcode($input{pc});
- $pretty_pc_text = $pretty_pc;
- $pretty_pc_text =~ s/ //g;
- $pretty_pc =~ s/ /&nbsp;/;
- }
-
- # truncate the lat,lon for nicer urls
- ( $lat, $lon ) = map { Utils::truncate_coordinate($_) } ( $lat, $lon );
-
- my $errors = '';
- $errors = '<ul class="error"><li>' . join('</li><li>', @errors) . '</li></ul>' if @errors;
-
- my $cobrand = Page::get_cobrand($q);
- my @types = (Cobrand::area_types($cobrand), @$mySociety::VotingArea::council_child_types);
- my %councils = map { $_ => 1 } Cobrand::area_types($cobrand);
-
- my $areas = mySociety::MaPit::call('point', "4326/$lon,$lat", type => \@types);
- my ($success, $error_msg) = Cobrand::council_check($cobrand, { all_councils => $areas }, $q, 'alert');
- if (!$success) {
- return alert_front_page($q, $error_msg);
- }
-
- return alert_front_page($q, _('That location does not appear to be covered by a council, perhaps it is offshore - please try somewhere more specific.')) if keys %$areas == 0;
-
- my ($options, $options_start, $options_end);
- if (mySociety::Config::get('COUNTRY') eq 'NO') {
-
- my (@options, $fylke, $kommune);
- foreach (values %$areas) {
- if ($_->{type} eq 'NKO') {
- $kommune = $_;
- } else {
- $fylke = $_;
- }
- }
- my $kommune_name = $kommune->{name};
- my $fylke_name = $fylke->{name};
-
- if ($fylke->{id} == 3) { # Oslo
-
- push @options, [ 'council', $fylke->{id}, Page::short_name($fylke),
- sprintf(_("Problems within %s"), $fylke_name) ];
-
- $options_start = "<div><ul id='rss_feed'>";
- $options = alert_list_options($q, @options);
- $options_end = "</ul>";
-
- } else {
-
- push @options,
- [ 'area', $kommune->{id}, Page::short_name($kommune), $kommune_name ],
- [ 'area', $fylke->{id}, Page::short_name($fylke), $fylke_name ];
- $options_start = '<div id="rss_list">';
- $options = $q->p($q->strong(_('Problems within the boundary of:'))) .
- $q->ul(alert_list_options($q, @options));
- @options = ();
- push @options,
- [ 'council', $kommune->{id}, Page::short_name($kommune), $kommune_name ],
- [ 'council', $fylke->{id}, Page::short_name($fylke), $fylke_name ];
- $options .= $q->p($q->strong(_('Or problems reported to:'))) .
- $q->ul(alert_list_options($q, @options));
- $options_end = $q->p($q->small(_('FixMyStreet sends different categories of problem
-to the appropriate council, so problems within the boundary of a particular council
-might not match the problems sent to that council. For example, a graffiti report
-will be sent to the district council, so will appear in both of the district
-council&rsquo;s alerts, but will only appear in the "Within the boundary" alert
-for the county council.'))) . '</div><div id="rss_buttons">';
-
- }
-
- } elsif (keys %$areas == 2) {
-
- # One-tier council
- my (@options, $council, $ward);
- foreach (values %$areas) {
- if ($councils{$_->{type}}) {
- $council = $_;
- } else {
- $ward = $_;
- }
- }
- my $council_name = $council->{name};
- my $ward_name = $ward->{name};
- push @options, [ 'council', $council->{id}, Page::short_name($council),
- sprintf(_("Problems within %s"), $council_name) ];
- push @options, [ 'ward', $council->{id}.':'.$ward->{id}, Page::short_name($council) . '/'
- . Page::short_name($ward), sprintf(_("Problems within %s ward"), $ward_name) ];
-
- $options_start = "<div><ul id='rss_feed'>";
- $options = alert_list_options($q, @options);
- $options_end = "</ul>";
-
- } elsif (keys %$areas == 1) {
-
- # One-tier council, no ward
- my (@options, $council);
- foreach (values %$areas) {
- $council = $_;
- }
- my $council_name = $council->{name};
- push @options, [ 'council', $council->{id}, Page::short_name($council),
- sprintf(_("Problems within %s"), $council_name) ];
-
- $options_start = "<div><ul id='rss_feed'>";
- $options = alert_list_options($q, @options);
- $options_end = "</ul>";
-
- } elsif (keys %$areas == 4) {
-
- # Two-tier council
- my (@options, $county, $district, $c_ward, $d_ward);
- foreach (values %$areas) {
- if ($_->{type} eq 'CTY') {
- $county = $_;
- } elsif ($_->{type} eq 'DIS') {
- $district = $_;
- } elsif ($_->{type} eq 'CED') {
- $c_ward = $_;
- } elsif ($_->{type} eq 'DIW') {
- $d_ward = $_;
- }
- }
- my $district_name = $district->{name};
- my $d_ward_name = $d_ward->{name};
- my $county_name = $county->{name};
- my $c_ward_name = $c_ward->{name};
- push @options,
- [ 'area', $district->{id}, Page::short_name($district), $district_name ],
- [ 'area', $district->{id}.':'.$d_ward->{id}, Page::short_name($district) . '/'
- . Page::short_name($d_ward), "$d_ward_name ward, $district_name" ],
- [ 'area', $county->{id}, Page::short_name($county), $county_name ],
- [ 'area', $county->{id}.':'.$c_ward->{id}, Page::short_name($county) . '/'
- . Page::short_name($c_ward), "$c_ward_name ward, $county_name" ];
- $options_start = '<div id="rss_list">';
- $options = $q->p($q->strong(_('Problems within the boundary of:'))) .
- $q->ul(alert_list_options($q, @options));
- @options = ();
- push @options,
- [ 'council', $district->{id}, Page::short_name($district), $district_name ],
- [ 'ward', $district->{id}.':'.$d_ward->{id}, Page::short_name($district) . '/' . Page::short_name($d_ward),
- "$district_name, within $d_ward_name ward" ];
- if ($q->{site} ne 'emptyhomes') {
- push @options,
- [ 'council', $county->{id}, Page::short_name($county), $county_name ],
- [ 'ward', $county->{id}.':'.$c_ward->{id}, Page::short_name($county) . '/'
- . Page::short_name($c_ward), "$county_name, within $c_ward_name ward" ];
- $options .= $q->p($q->strong(_('Or problems reported to:'))) .
- $q->ul(alert_list_options($q, @options));
- $options_end = $q->p($q->small(_('FixMyStreet sends different categories of problem
-to the appropriate council, so problems within the boundary of a particular council
-might not match the problems sent to that council. For example, a graffiti report
-will be sent to the district council, so will appear in both of the district
-council&rsquo;s alerts, but will only appear in the "Within the boundary" alert
-for the county council.'))) . '</div><div id="rss_buttons">';
- } else {
- $options_end = '';
- }
- } else {
- # Hopefully impossible in the UK!
- throw Error::Simple('An area with three tiers of council? Impossible! '. $lat . ' ' . $lon . ' ' . join('|',keys %$areas));
- }
-
- my $dist = mySociety::Gaze::get_radius_containing_population($lat, $lon, 200000);
- $dist = int($dist * 10 + 0.5);
- $dist = $dist / 10.0;
-
- my $checked = '';
- $checked = ' checked' if $q->param('feed') && $q->param('feed') eq "local:$lat:$lon";
- my $cobrand_form_elements = Cobrand::form_elements($cobrand, 'alerts', $q);
- my $pics = Cobrand::recent_photos($cobrand, 5, $lat, $lon, $dist);
- $pics = '<div id="alert_photos">' . $q->h2(_('Photos of recent nearby reports')) . $pics . '</div>' if $pics;
- my $header;
- if ($pretty_pc) {
- $header = sprintf(_('Local RSS feeds and email alerts for &lsquo;%s&rsquo;'), $pretty_pc);
- } else {
- $header = _('Local RSS feeds and email alerts');
- }
- my $out = $q->h1($header);
- my $form_action = Cobrand::url($cobrand, '/alert', $q);
- $out .= <<EOF;
-<form id="alerts" name="alerts" method="post" action="$form_action">
-<input type="hidden" name="type" value="local">
-<input type="hidden" name="pc" value="$input_h{pc}">
-$cobrand_form_elements
-$pics
-
-EOF
- $out .= $q->p(($pretty_pc ? sprintf(_('Here are the types of local problem alerts for &lsquo;%s&rsquo;.'), $pretty_pc)
- : '') . ' ' . _('Select which type of alert you&rsquo;d like and click the button for an RSS
-feed, or enter your email address to subscribe to an email alert.'));
- $out .= $errors;
- $out .= $q->p(_('The simplest alert is our geographic one:'));
- my $rss_label = sprintf(_('Problems within %skm of this location'), $dist);
- $out .= <<EOF;
-<p id="rss_local">
-<input type="radio" name="feed" id="local:$lat:$lon" value="local:$lat:$lon"$checked>
-<label for="local:$lat:$lon">$rss_label</label>
-EOF
- my $rss_feed;
- if ($pretty_pc_text) {
- $rss_feed = Cobrand::url($cobrand, "/rss/pc/$pretty_pc_text", $q);
- } else {
- $rss_feed = Cobrand::url($cobrand, "/rss/l/$lat,$lon", $q);
- }
-
- my $default_link = Cobrand::url($cobrand, "/alert?type=local;feed=local:$lat:$lon", $q);
- my $rss_details = _('(a default distance which covers roughly 200,000 people)');
- $out .= $rss_details;
- $out .= " <a href='$rss_feed'><img src='/i/feed.png' width='16' height='16' title='"
- . _('RSS feed of nearby problems') . "' alt='" . _('RSS feed') . "' border='0'></a>";
- $out .= '</p> <p id="rss_local_alt">' . _('(alternatively the RSS feed can be customised, within');
- my $rss_feed_2k = Cobrand::url($cobrand, $rss_feed.'/2', $q);
- my $rss_feed_5k = Cobrand::url($cobrand, $rss_feed.'/5', $q);
- my $rss_feed_10k = Cobrand::url($cobrand, $rss_feed.'/10', $q);
- my $rss_feed_20k = Cobrand::url($cobrand, $rss_feed.'/20', $q);
- $out .= <<EOF;
- <a href="$rss_feed_2k">2km</a> / <a href="$rss_feed_5k">5km</a>
-/ <a href="$rss_feed_10k">10km</a> / <a href="$rss_feed_20k">20km</a>)
-</p>
-EOF
- $out .= $q->p(_('Or you can subscribe to an alert based upon what ward or council you&rsquo;re in:'));
- $out .= $options_start;
- $out .= $options;
- $out .= $options_end;
- $out .= $q->p('<input type="submit" name="rss" value="' . _('Give me an RSS feed') . '">');
- $out .= $q->p({-id=>'alert_or'}, _('or'));
- $out .= '<p>' . _('Your email:') . ' <input type="text" id="rznvy" name="rznvy" value="' . $input_h{rznvy} . '" size="30"></p>
-<p><input type="submit" name="alert" value="' . _('Subscribe me to an email alert') . '"></p>
-</div>
-</form>';
- my %vars = (header => $header,
- cobrand_form_elements => $cobrand_form_elements,
- error => $errors,
- rss_label => $rss_label,
- rss_feed => $rss_feed,
- default_link => $default_link,
- rss_details => $rss_details,
- rss_feed_2k => $rss_feed_2k,
- rss_feed_5k => $rss_feed_5k,
- rss_feed_10k => $rss_feed_10k,
- rss_feed_20k => $rss_feed_20k,
- lat => $lat,
- lon => $lon,
- options => $options );
- my $cobrand_page = Page::template_include('alert-options', $q, Page::template_root($q), %vars);
- $out = $cobrand_page if ($cobrand_page);
- return $out;
-}
-
-sub alert_list_options {
- my $q = shift;
- my $out = '';
- my $feed = $q->param('feed') || '';
- my $cobrand = Page::get_cobrand($q);
- my $cobrand_list = Cobrand::alert_list_options($cobrand, $q, @_);
- return $cobrand_list if ($cobrand_list);
- foreach (@_) {
- my ($type, $vals, $rss, $text) = @$_;
- (my $vals2 = $rss) =~ tr{/+}{:_};
- my $id = $type . ':' . $vals . ':' . $vals2;
- $out .= '<li><input type="radio" name="feed" id="' . $id . '" ';
- $out .= 'checked ' if $feed eq $id;
- my $url = "/rss/";
- $url .= $type eq 'area' ? 'area' : 'reports';
- $url .= '/' . $rss ;
- my $rss_url = Cobrand::url($cobrand, $url, $q);
- $out .= 'value="' . $id . '"> <label for="' . $id . '">' . $text
- . '</label> <a href="' . $rss_url . '"><img src="/i/feed.png" width="16" height="16"
-title="' . sprintf(_('RSS feed of %s'), $text) . '" alt="' . _('RSS feed') . '" border="0"></a></li>';
- }
- return $out;
-}
-
-sub alert_front_page {
- my $q = shift;
- my $cobrand = Page::get_cobrand($q);
- my $error = shift;
- my $errors = '';
- $errors = '<ul class="error"><li>' . $error . '</li></ul>' if $error;
-
- my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } qw(pc);
- my $header = _('Local RSS feeds and email alerts');
- my $intro = _('FixMyStreet has a variety of RSS feeds and email alerts for local problems, including
-alerts for all problems within a particular ward or council, or all problems
-within a certain distance of a particular location.');
- my $pc_label = _('To find out what local alerts we have for you, please enter your GB
-postcode or street name and area:');
- my $form_action = Cobrand::url(Page::get_cobrand($q), '/alert', $q);
- my $cobrand_form_elements = Cobrand::form_elements($cobrand, 'alerts', $q);
- my $cobrand_extra_data = Cobrand::extra_data($cobrand, $q);
- my $submit_text = _('Go');
-
- my $out = $q->h1($header);
- $out .= $q->p($intro);
- $out .= $errors . qq(<form method="get" action="$form_action">);
- $out .= $q->p($pc_label, '<input type="text" name="pc" value="' . $input_h{pc} . '">
-<input type="submit" value="' . $submit_text . '">');
- $out .= $cobrand_form_elements;
- $out .= '</form>';
-
- my %vars = (error => $error,
- header => $header,
- intro => $intro,
- pc_label => $pc_label,
- form_action => $form_action,
- input_h => \%input_h,
- submit_text => $submit_text,
- cobrand_form_elements => $cobrand_form_elements,
- cobrand_extra_data => $cobrand_extra_data,
- url_home => Cobrand::url($cobrand, '/', $q));
-
- my $cobrand_page = Page::template_include('alert-front-page', $q, Page::template_root($q), %vars);
- $out = $cobrand_page if ($cobrand_page);
-
- return $out if $q->referer() && $q->referer() =~ /fixmystreet\.com/;
- my $recent_photos = Cobrand::recent_photos($cobrand, 10);
- $out .= '<div id="alert_recent">' . $q->h2(_('Some photos of recent reports')) . $recent_photos . '</div>' if $recent_photos;
-
- return $out;
-}
-
-sub alert_rss {
- my $q = shift;
- my $feed = $q->param('feed');
- return alert_list($q, _('Please select the feed you want')) unless $feed;
- my $cobrand = Page::get_cobrand($q);
- my $base_url = Cobrand::base_url($cobrand);
- my $extra_params = Cobrand::extra_params($cobrand, $q);
- my $url;
- if ($feed =~ /^area:(?:\d+:)+(.*)$/) {
- (my $id = $1) =~ tr{:_}{/+};
- $url = $base_url . '/rss/area/' . $id;
- $url .= "?" . $extra_params if ($extra_params);
- print $q->redirect($url);
- return;
- } elsif ($feed =~ /^(?:council|ward):(?:\d+:)+(.*)$/) {
- (my $id = $1) =~ tr{:_}{/+};
- $url = $base_url . '/rss/reports/' . $id;
- $url .= "?" . $extra_params if ($extra_params);
- print $q->redirect($url);
- return;
- } elsif ($feed =~ /^local:([\d\.-]+):([\d\.-]+)$/) {
- $url = $base_url . '/rss/l/' . $1 . ',' . $2;
- $url .= "?" . $extra_params if ($extra_params);
- print $q->redirect($url);
- return;
- } else {
- return alert_list($q, _('Illegal feed selection'));
- }
-}
-
-sub alert_updates_form {
- my ($q, @errors) = @_;
- my @vars = qw(id rznvy);
- my %input = map { $_ => $q->param($_) || '' } @vars;
- my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
- my $cobrand_form_elements = Cobrand::form_elements(Page::get_cobrand($q), 'alerts', $q);
- my $out = '';
- if (@errors) {
- $out .= '<ul class="error"><li>' . join('</li><li>', @errors) . '</li></ul>';
- }
- $out .= $q->p(_('Receive email when updates are left on this problem.'));
- my $label = _('Email:');
- my $subscribe = _('Subscribe');
- my $form_action = Cobrand::url(Page::get_cobrand($q), 'alert', $q);
- $out .= <<EOF;
-<form action="$form_action" method="post">
-<label class="n" for="alert_rznvy">$label</label>
-<input type="text" name="rznvy" id="alert_rznvy" value="$input_h{rznvy}" size="30">
-<input type="hidden" name="id" value="$input_h{id}">
-<input type="hidden" name="type" value="updates">
-<input type="submit" value="$subscribe">
-$cobrand_form_elements
-</form>
-EOF
- return $out;
-}
-
-sub alert_local_form {
- my ($q, @errors) = @_;
- my @vars = qw(id rznvy feed);
- my %input = map { $_ => $q->param($_) || '' } @vars;
- my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
- my $cobrand_form_elements = Cobrand::form_elements(Page::get_cobrand($q), 'alerts', $q);
- my $out = '';
- if (@errors) {
- $out .= '<ul class="error"><li>' . join('</li><li>', @errors) . '</li></ul>';
- }
- $out .= $q->p(_('Receive alerts on new local problems'));
- my $label = _('Email:');
- my $subscribe = _('Subscribe');
- my $form_action = Cobrand::url(Page::get_cobrand($q), 'alert', $q);
- $out .= <<EOF;
-<form action="$form_action" method="post">
-<label class="n" for="alert_rznvy">$label</label>
-<input type="text" name="rznvy" id="alert_rznvy" value="$input_h{rznvy}" size="30">
-<input type="hidden" name="feed" value="$input_h{feed}">
-<input type="hidden" name="type" value="local">
-<input type="submit" value="$subscribe">
-$cobrand_form_elements
-</form>
-EOF
- return $out;
-}
-
-sub alert_signed_input {
- my $q = shift;
- my ($salt, $signed_email) = split /,/, $q->param('signed_email');
- my $email = $q->param('rznvy');
- my $id = $q->param('id');
- my $secret = scalar(dbh()->selectrow_array('select secret from secret'));
- my $out;
- my $cobrand = Page::get_cobrand($q);
- if ($signed_email eq sha1_hex("$id-$email-$salt-$secret")) {
- my $alert_id = FixMyStreet::Alert::create($email, 'new_updates', $cobrand, '', $id);
- FixMyStreet::Alert::confirm($alert_id);
- $out = $q->p(_('You have successfully subscribed to that alert.'));
- my $cobrand = Page::get_cobrand($q);
- my $display_advert = Cobrand::allow_crosssell_adverts($cobrand);
- if ($display_advert) {
- $out .= CrossSell::display_advert($q, $email);
- }
- } else {
- $out = $q->p(_('We could not validate that alert.'));
- }
- return $out;
-}
-
-sub alert_token {
- my ($q, $data) = @_;
- my $id = $data->{id};
- my $type = $data->{type};
- my $email = $data->{email};
-
- (my $domain = $email) =~ s/^.*\@//;
- if (dbh()->selectrow_array('select email from abuse where lower(email)=? or lower(email)=?', {}, lc($email), lc($domain))) {
- return $q->p('Sorry, there has been an error confirming your alert.');
- }
-
- my $out;
- my $cobrand = Page::get_cobrand($q);
- my $message;
- my $display_advert = Cobrand::allow_crosssell_adverts($cobrand);
- if ($type eq 'subscribe') {
- FixMyStreet::Alert::confirm($id);
- $message = _('You have successfully confirmed your alert.');
- $out = $q->p($message);
- if ($display_advert) {
- $out .= CrossSell::display_advert($q, $email);
- }
- } elsif ($type eq 'unsubscribe') {
- FixMyStreet::Alert::delete($id);
- $message = _('You have successfully deleted your alert.');
- $out = $q->p($message);
- if ($display_advert) {
- $out .= CrossSell::display_advert($q, $email);
- }
- }
-
- my %vars = (message => $message,
- url_home => Cobrand::url($cobrand, '/', $q));
- my $confirmation = Page::template_include('confirmed-alert', $q, Page::template_root($q), %vars);
- return $confirmation if $confirmation;
- return $out;
-}
-
-sub alert_do_subscribe {
- my ($q, $email) = @_;
-
- my $type = $q->param('type');
-
- my @errors;
- push @errors, _('Please enter a valid email address') unless is_valid_email($email);
- push @errors, _('Please select the type of alert you want') if $type && $type eq 'local' && !$q->param('feed');
- if (@errors) {
- return alert_updates_form($q, @errors) if $type && $type eq 'updates';
- return alert_list($q, @errors) if $type && $type eq 'local';
- return alert_front_page($q, @errors);
- }
-
- my $alert_id;
- my $cobrand = Page::get_cobrand($q);
- my $cobrand_data = Cobrand::extra_alert_data($cobrand, $q);
- if ($type eq 'updates') {
- my $id = $q->param('id');
- $alert_id = FixMyStreet::Alert::create($email, 'new_updates', $cobrand, $cobrand_data, $id);
- } elsif ($type eq 'problems') {
- $alert_id = FixMyStreet::Alert::create($email, 'new_problems', $cobrand, $cobrand_data);
- } elsif ($type eq 'local') {
- my $feed = $q->param('feed');
- if ($feed =~ /^area:(?:\d+:)?(\d+)/) {
- $alert_id = FixMyStreet::Alert::create($email, 'area_problems', $cobrand, $cobrand_data, $1);
- } elsif ($feed =~ /^council:(\d+)/) {
- $alert_id = FixMyStreet::Alert::create($email, 'council_problems', $cobrand, $cobrand_data, $1, $1);
- } elsif ($feed =~ /^ward:(\d+):(\d+)/) {
- $alert_id = FixMyStreet::Alert::create($email, 'ward_problems', $cobrand, $cobrand_data, $1, $2);
- } elsif ($feed =~ m{ \A local: ( [\+\-]? \d+ \.? \d* ) : ( [\+\-]? \d+ \.? \d* ) }xms ) {
- my $lat = $1;
- my $lon = $2;
- $alert_id = FixMyStreet::Alert::create($email, 'local_problems', $cobrand, $cobrand_data, $lon, $lat);
- }
- } else {
- throw FixMyStreet::Alert::Error('Invalid type');
- }
-
- my %h = ();
- $h{url} = Page::base_url_with_lang($q, undef, 1) . '/A/'
- . mySociety::AuthToken::store('alert', { id => $alert_id, type => 'subscribe', email => $email } );
- dbh()->commit();
- return Page::send_confirmation_email($q, $email, undef, 'alert', %h);
-}
-
diff --git a/web/cobrands/.gitignore b/web/cobrands/.gitignore
deleted file mode 100644
index 911c95650..000000000
--- a/web/cobrands/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/cities \ No newline at end of file
diff --git a/web/css/cobrands/emptyhomes/emptyhomes.css b/web/cobrands/emptyhomes/css.css
index f6047ebe5..f6047ebe5 100644
--- a/web/css/cobrands/emptyhomes/emptyhomes.css
+++ b/web/cobrands/emptyhomes/css.css
diff --git a/web/confirm.cgi b/web/confirm.cgi
deleted file mode 100755
index c4a37c67f..000000000
--- a/web/confirm.cgi
+++ /dev/null
@@ -1,247 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# confirm.cgi:
-# Confirmation code for FixMyStreet
-#
-# Copyright (c) 2006 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: confirm.cgi,v 1.66 2009-12-15 18:26:11 louise Exp $
-
-use strict;
-use Standard;
-use Digest::SHA1 qw(sha1_hex);
-use CrossSell;
-use FixMyStreet::Alert;
-use mySociety::AuthToken;
-use mySociety::Random qw(random_bytes);
-
-sub main {
- my $q = shift;
- my $cobrand = Page::get_cobrand($q);
- my $out = '';
- my $token = $q->param('token');
- my $type = $q->param('type') || '';
- my $tokentype = $type eq 'questionnaire' ? 'update' : $type;
- my $data = mySociety::AuthToken::retrieve($tokentype, $token);
- if ($data) {
- if ($type eq 'update') {
- $out = confirm_update($q, $data);
- } elsif ($type eq 'problem') {
- $out = confirm_problem($q, $data);
- } elsif ($type eq 'questionnaire') {
- $out = add_questionnaire($q, $data, $token);
- }
- dbh()->commit();
- } else {
- my $contact_url = Cobrand::url($cobrand, '/contact', $q);
- $out = $q->p(sprintf(_(<<EOF), $contact_url));
-Thank you for trying to confirm your update or problem. We seem to have an
-error ourselves though, so <a href="%s">please let us know what went on</a>
-and we'll look into it.
-EOF
-
- my %vars = (error => $out);
- my $cobrand_page = Page::template_include('error', $q, Page::template_root($q), %vars);
- $out = $cobrand_page if $cobrand_page;
- }
-
- print Page::header($q, title=>_('Confirmation'));
- print $out;
- print Page::footer($q);
-}
-Page::do_fastcgi(\&main);
-
-sub confirm_update {
- my ($q, $data) = @_;
- my $cobrand = Page::get_cobrand($q);
- my $id = $data;
- my $add_alert = 0;
- if (ref($data)) {
- $id = $data->{id};
- $add_alert = $data->{add_alert};
- }
-
- my ($problem_id, $fixed, $email, $name, $cobrand_data) = dbh()->selectrow_array(
- "select problem_id, mark_fixed, email, name, cobrand_data from comment where id=?", {}, $id);
- $email = lc($email);
-
- (my $domain = $email) =~ s/^.*\@//;
- if (dbh()->selectrow_array('select email from abuse where lower(email)=? or lower(email)=?', {}, $email, $domain)) {
- dbh()->do("update comment set state='hidden' where id=?", {}, $id);
- return $q->p('Sorry, there has been an error confirming your update.');
- } else {
- dbh()->do("update comment set state='confirmed', confirmed=ms_current_timestamp() where id=? and state='unconfirmed'", {}, $id);
- }
-
- my $creator_fixed = 0;
- if ($fixed) {
- dbh()->do("update problem set state='fixed', lastupdate = ms_current_timestamp()
- where id=? and state='confirmed'", {}, $problem_id);
- # If a problem reporter is marking their own problem as fixed, turn off questionnaire sending
- $creator_fixed = dbh()->do("update problem set send_questionnaire='f' where id=? and lower(email)=?
- and send_questionnaire='t'", {}, $problem_id, $email);
- } else {
- # Only want to refresh problem if not already fixed
- dbh()->do("update problem set lastupdate = ms_current_timestamp()
- where id=? and state='confirmed'", {}, $problem_id);
- }
-
- my $out = '';
- if ($creator_fixed > 0 && $q->{site} ne 'emptyhomes') {
- my $answered_ever_reported = dbh()->selectrow_array(
- 'select id from questionnaire where problem_id in (select id from problem where lower(email)=?) and ever_reported is not null', {}, $email);
- if (!$answered_ever_reported) {
- $out = ask_questionnaire($q->param('token'), $q);
- }
- }
-
- my $report_url = Cobrand::url($cobrand, "/report/$problem_id#update_$id", $q);
- if (!$out) {
- $out = $q->p({class => 'confirmed'}, sprintf(_('You have successfully confirmed your update and you can now <a href="%s">view it on the site</a>.'), $report_url));
- my $display_advert = Cobrand::allow_crosssell_adverts($cobrand);
- if ($display_advert) {
- $out .= CrossSell::display_advert($q, $email, $name);
- }
- my %vars = (
- url_report => $report_url,
- url_home => Cobrand::url($cobrand, '/', $q),
- );
- my $cobrand_page = Page::template_include('confirmed-update', $q, Page::template_root($q), %vars);
- $out = $cobrand_page if $cobrand_page;
- }
-
- # Subscribe updater to email updates if requested
- if ($add_alert) {
- my $alert_id = FixMyStreet::Alert::create($email, 'new_updates', $cobrand, $cobrand_data, $problem_id);
- FixMyStreet::Alert::confirm($alert_id);
- }
-
- return $out;
-}
-
-sub confirm_problem {
- my ($q, $id) = @_;
- my $cobrand = Page::get_cobrand($q);
- my ($council, $email, $name, $cobrand_data) = dbh()->selectrow_array("select council, email, name, cobrand_data from problem where id=?", {}, $id);
-
- (my $domain = $email) =~ s/^.*\@//;
- if (dbh()->selectrow_array('select email from abuse where lower(email)=? or lower(email)=?', {}, lc($email), lc($domain))) {
- dbh()->do("update problem set state='hidden', lastupdate=ms_current_timestamp() where id=?", {}, $id);
- return $q->p(_('Sorry, there has been an error confirming your problem.'));
- } else {
- dbh()->do("update problem set state='confirmed', confirmed=ms_current_timestamp(), lastupdate=ms_current_timestamp()
- where id=? and state='unconfirmed'", {}, $id);
- }
- my $out;
- if ($q->{site} eq 'emptyhomes') {
- if ($council) {
- $out = $q->p(_('Thank you for reporting an empty property on
-ReportEmptyHomes.com. We have emailed the lead officer for empty homes in the council
-responsible with details, and asked them to do whatever they can to get the
-empty property back into use as soon as possible.')) .
-$q->p(_('It is worth noting however that the process can sometimes be slow,
-especially if the property is in very poor repair or the owner is unwilling to
-act. In most cases it can take six months or more before you can expect to see
-anything change and sometimes there may be considerable barries to a property
-being brought back into use. This doesn&rsquo;t mean the council isn&rsquo;t
-doing anything. We encourage councils to update the website so you can
-see what is happening. It may be a long process, but you reporting your
-concerns about this property to the council is a valuable first step.')) .
-$q->p(_('We may contact you periodically to ask if anything has changed
-with the property you reported.')) .
-$q->p(_('Thank you for using ReportEmptyHomes.com. Your action is already helping
-to resolve the UK&rsquo;s empty homes crisis.')) .
-$q->p('<a href="/report/' . $id . '">' . _('View your report') . '</a>.');
- } else {
- $out = $q->p(_('Thank you for reporting this empty property on ReportEmptyHomes.com.
-At present the report cannot be sent through to the council for this area. We
-are working with councils to link them into the system so that as many areas
-as possible will be covered.')) .
-$q->p('<a href="/report/' . $id . '">' . _('View your report') . '</a>.');
- }
- } else {
- my $report_url = Cobrand::url($cobrand, "/report/$id", $q);
- $out = $q->p({class => 'confirmed'},
- _('You have successfully confirmed your problem')
- . ($council ? _(' and <strong>we will now send it to the council</strong>') : '')
- . sprintf(_('. You can <a href="%s">view the problem on this site</a>.'), $report_url)
- );
- my $display_advert = Cobrand::allow_crosssell_adverts($cobrand);
- if ($display_advert) {
- $out .= CrossSell::display_advert($q, $email, $name);
- }
- my %vars = (
- url_report => $report_url,
- url_home => Cobrand::url($cobrand, '/', $q),
- );
- my $cobrand_page = Page::template_include('confirmed-problem', $q, Page::template_root($q), %vars);
- $out = $cobrand_page if $cobrand_page;
- }
-
- # Subscribe problem reporter to email updates
- my $alert_id = FixMyStreet::Alert::create($email, 'new_updates', $cobrand, $cobrand_data, $id);
- FixMyStreet::Alert::confirm($alert_id);
-
- return $out;
-}
-
-sub ask_questionnaire {
- my ($token, $q) = @_;
- my $cobrand = Page::get_cobrand($q);
- my $qn_thanks = _("Thanks, glad to hear it's been fixed! Could we just ask if you have ever reported a problem to a council before?");
- my $yes = _('Yes');
- my $no = _('No');
- my $go = _('Submit');
- my $form_action = Cobrand::url($cobrand, "/confirm", $q);
- my $form_extra_elements = Cobrand::form_elements($cobrand, 'questionnaire', $q);
- my $out = <<EOF;
-<form action="$form_action" method="post" id="questionnaire">
-<input type="hidden" name="type" value="questionnaire">
-<input type="hidden" name="token" value="$token">
-<p>$qn_thanks</p>
-<p align="center">
-<input type="radio" name="reported" id="reported_yes" value="Yes">
-<label for="reported_yes">$yes</label>
-<input type="radio" name="reported" id="reported_no" value="No">
-<label for="reported_no">$no</label>
-$form_extra_elements
-<input type="submit" value="$go">
-</p>
-</form>
-EOF
- my %vars = (form => $out,
- url_home => Cobrand::url($cobrand, '/', $q));
- my $cobrand_template = Page::template_include('update-questionnaire', $q, Page::template_root($q), %vars);
- $out = $cobrand_template if $cobrand_template;
- return $out;
-}
-
-sub add_questionnaire {
- my ($q, $data, $token) = @_;
-
- my $id = $data;
- if (ref($data)) {
- $id = $data->{id};
- }
- my $cobrand = Page::get_cobrand($q);
- my ($problem_id, $email, $name) = dbh()->selectrow_array("select problem_id, email, name from comment where id=?", {}, $id);
- my $reported = $q->param('reported') || '';
- $reported = $reported eq 'Yes' ? 't' : ($reported eq 'No' ? 'f' : undef);
- return ask_questionnaire($token, $q) unless $reported;
- my $already = dbh()->selectrow_array("select id from questionnaire
- where problem_id=? and old_state='confirmed' and new_state='fixed'",
- {}, $problem_id);
- dbh()->do("insert into questionnaire (problem_id, whensent, whenanswered,
- ever_reported, old_state, new_state) values (?, ms_current_timestamp(),
- ms_current_timestamp(), ?, 'confirmed', 'fixed');", {}, $problem_id, $reported)
- unless $already;
- my $report_url = Cobrand::url($cobrand, "/report/$problem_id", $q);
- my $out = $q->p({class => 'confirmed'}, sprintf(_('Thank you &mdash; you can <a href="%s">view your updated problem</a> on the site.'), $report_url));
- my $display_advert = Cobrand::allow_crosssell_adverts($cobrand);
- if ($display_advert) {
- $out .= CrossSell::display_advert($q, $email, $name);
- }
- return $out;
-}
-
diff --git a/web/contact.cgi b/web/contact.cgi
deleted file mode 100755
index b3af1688b..000000000
--- a/web/contact.cgi
+++ /dev/null
@@ -1,245 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# contact.cgi:
-# Contact page for FixMyStreet
-#
-# Copyright (c) 2006 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: contact.cgi,v 1.58 2009-12-17 14:57:34 louise Exp $
-
-use strict;
-use Standard;
-use CrossSell;
-use mySociety::Email;
-use mySociety::EmailUtil;
-use mySociety::Web qw(ent);
-use mySociety::Random qw(random_bytes);
-
-# Main code for index.cgi
-sub main {
- my $q = shift;
- print Page::header($q, title=>_('Contact Us'), context=>'contact', robots => 'noindex,nofollow');
- my $out = '';
- if ($q->param('submit_form')) {
- $out = contact_submit($q);
- } else {
- $out = contact_page($q, [], {});
- }
- print $out;
- print Page::footer($q);
-}
-Page::do_fastcgi(\&main);
-
-sub contact_submit {
- my $q = shift;
- my @vars = qw(name em subject message id update_id);
- my %input = map { $_ => $q->param($_) || '' } @vars;
- my $cobrand = Page::get_cobrand($q);
- my @errors;
- my %field_errors;
- $field_errors{name} = _('Please give your name') unless $input{name} =~ /\S/;
- if ($input{em} !~ /\S/) {
- $field_errors{email} = _('Please give your email');
- } elsif (!mySociety::EmailUtil::is_valid_email($input{em})) {
- $field_errors{email} = _('Please give a valid email address');
- }
- $field_errors{subject} = _('Please give a subject') unless $input{subject} =~ /\S/;
- $field_errors{message} = _('Please write a message') unless $input{message} =~ /\S/;
- push(@errors, _('Illegal ID')) if (($input{id} && $input{id} !~ /^[1-9]\d*$/) || ($input{update_id} && $input{update_id} !~ /^[1-9]\d*$/));
- return contact_page($q, \@errors, \%field_errors) if (@errors || scalar keys %field_errors);
-
- (my $message = $input{message}) =~ s/\r\n/\n/g;
- (my $subject = $input{subject}) =~ s/\r|\n/ /g;
- my $extra_data = Cobrand::extra_data($cobrand, $q);
- my $base_url = Cobrand::base_url_for_emails($cobrand, $extra_data);
- my $admin_base_url = Cobrand::admin_base_url($cobrand);
- if (!$admin_base_url) {
- $admin_base_url = "https://secure.mysociety.org/admin/bci/";
- }
- if ($input{id} && $input{update_id}) {
- $message .= "\n\n[ Complaint about update $input{update_id} on report $input{id} - "
- . $base_url . "/report/$input{id}#update_$input{update_id} - "
- . "$admin_base_url?page=update_edit;id=$input{update_id} ]";
- } elsif ($input{id}) {
- $message .= "\n\n[ Complaint about report $input{id} - "
- . $base_url . "/report/$input{id} - "
- . "$admin_base_url?page=report_edit;id=$input{id} ]";
- }
- my $postfix = '[ Sent by contact.cgi on ' .
- $ENV{'HTTP_HOST'} . '. ' .
- "IP address " . $ENV{'REMOTE_ADDR'} .
- ($ENV{'HTTP_X_FORWARDED_FOR'} ? ' (forwarded from '.$ENV{'HTTP_X_FORWARDED_FOR'}.')' : '') . '. ' .
- ' ]';
-
- my $recipient = Cobrand::contact_email($cobrand);
- my $recipient_name = Cobrand::contact_name($cobrand);
- my $email = mySociety::Email::construct_email({
- _body_ => "$message\n\n$postfix",
- From => [$input{em}, $input{name}],
- To => [[$recipient, _($recipient_name)]],
- Subject => 'FMS message: ' . $subject,
- 'Message-ID' => sprintf('<contact-%s-%s@mysociety.org>', time(), unpack('h*', random_bytes(5, 1))),
- });
- my $result = mySociety::EmailUtil::send_email($email, $input{em}, $recipient);
- if ($result == mySociety::EmailUtil::EMAIL_SUCCESS) {
- my $message = _("Thanks for your feedback. We'll get back to you as soon as we can!");
- my $out = $q->p($message);
- my $display_advert = Cobrand::allow_crosssell_adverts($cobrand);
- my $advert = '';
- if ($display_advert) {
- $advert = CrossSell::display_advert($q, $input{em}, $input{name}, emailunvalidated=>1 );
- $out .= $advert;
- }
- my %vars = (message => $message,
- advert => $advert);
- my $template_out = Page::template_include('confirmed-alert', $q, Page::template_root($q), %vars);
- return $template_out if $template_out;
- return $out;
- } else {
- return $q->p('Failed to send message. Please try again, or <a href="mailto:' . $recipient . '">email us</a>.');
- }
-}
-
-sub contact_details {
- my ($q) = @_;
- my $out = '';
- my $sitename = _('FixMyStreet');
- my $contact_info = '';
- if ( mySociety::Config::get('COUNTRY') eq 'GB' ) {
- # XXX Rewrite to make brandable?
- $contact_info .= <<EOF;
-<div class="contact-details">
-<p>$sitename is a service provided by mySociety, which is the project of a
-registered charity. The charity is called UK Citizens Online Democracy and is charity number 1076346.</p>
-<p>mySociety can be contacted by email at <a href="mailto:hello&#64;mysociety.org">hello&#64;mysociety.org</a>,
-or by post at:</p>
-<p>mySociety<br>
-483 Green Lanes<br>
-London<br>
-N13 4BS<br>
-UK</p>
-</div>
-EOF
- $out .= $contact_info unless $q->{site} eq 'emptyhomes';
- }
- return $out;
-}
-
-sub generic_contact_text {
- my ($q) = @_;
- my $intro;
-
- if ($q->{site} eq 'emptyhomes') {
- $intro .= $q->p(_('We&rsquo;d love to hear what you think about this
-website. Just fill in the form. Please don&rsquo;t contact us about individual empty
-homes; use the box accessed from <a href="/">the front page</a>.'));
- } else {
- my $cobrand = Page::get_cobrand($q);
- my $mailto = Cobrand::contact_email($cobrand);
- $mailto =~ s/\@/&#64;/;
- $intro .= $q->p(_('Please do <strong>not</strong> report problems through this form; messages go to
-the team behind FixMyStreet, not a council. To report a problem,
-please <a href="/">go to the front page</a> and follow the instructions.'));
- $intro .= $q->p(sprintf(_("We'd love to hear what you think about this site. Just fill in the form, or send an email to <a href='mailto:%s'>%s</a>:"), $mailto, $mailto));
- }
- return $intro;
-}
-
-sub contact_page {
- my ($q, $errors, $field_errors) = @_;
- my @errors = @$errors;
- my %field_errors = %{$field_errors};
- push @errors, _('There were problems with your report. Please see below.') if (scalar keys %field_errors);
- my @vars = qw(name em subject message);
- my %input = map { $_ => $q->param($_) || '' } @vars;
- my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
- my $out = '';
- my $header = _('Contact the team');
- $errors = '';
-
- if (@errors) {
- $errors = '<ul class="error"><li>' . join('</li><li>', @errors) . '</li></ul>';
- }
- my $cobrand = Page::get_cobrand($q);
- my $form_action = Cobrand::url($cobrand, '/contact', $q);
-
- my $intro = '';
- my $item_title = '';
- my $item_body = '';
- my $item_meta = '';
- my $hidden_vals = '';
- my $id = $q->param('id');
- my $update_id = $q->param('update_id');
- $id = undef unless $id && $id =~ /^[1-9]\d*$/;
- $update_id = undef unless $update_id && $update_id =~ /^[1-9]\d*$/;
- if ($id) {
- mySociety::DBHandle::configure(
- Name => mySociety::Config::get('BCI_DB_NAME'),
- User => mySociety::Config::get('BCI_DB_USER'),
- Password => mySociety::Config::get('BCI_DB_PASS'),
- Host => mySociety::Config::get('BCI_DB_HOST', undef),
- Port => mySociety::Config::get('BCI_DB_PORT', undef)
- );
- my $p = dbh()->selectrow_hashref(
- 'select title,detail,name,anonymous,extract(epoch from confirmed) as confirmed
- from problem where id=?', {}, $id);
- if ($update_id) {
- my $u = dbh()->selectrow_hashref(
- 'select comment.text, comment.name, problem.title, extract(epoch from comment.confirmed) as confirmed
- from comment, problem where comment.id=?
- and comment.problem_id = problem.id
- and comment.problem_id=?', {}, $update_id ,$id);
- if (! $u) {
- $intro = generic_contact_text($q);
- } else {
- $intro .= $q->p(_('You are reporting the following update for being abusive, containing personal information, or similar:'));
- $item_title = ent($u->{title});
- $item_meta = $q->em( 'Update below added ', (!$u->{name}) ? 'anonymously' : "by " . ent($u->{name}),
- ' at ' . Page::prettify_epoch($q, $u->{confirmed}));
- $item_body = ent($u->{text});
- $hidden_vals .= '<input type="hidden" name="update_id" value="' . $update_id . '">';
- }
- } else {
- if (! $p) {
- $intro = generic_contact_text($q);
- } else {
- $intro .= $q->p(_('You are reporting the following problem report for being abusive, containing personal information, or similar:'));
- $item_title = ent($p->{title});
- my $date_time = Page::prettify_epoch($q, $p->{confirmed});
- $item_meta = $q->em(
- $p->{anonymous}
- ? sprintf(_('Reported anonymously at %s'), $date_time)
- : sprintf(_('Reported by %s at %s'), ent($p->{name}), $date_time)
- );
- $item_body = ent($p->{detail});
- }
- }
- $hidden_vals .= '<input type="hidden" name="id" value="' . $id . '">';
- } else {
- $intro = generic_contact_text($q);
- }
- my $cobrand_form_elements = Cobrand::form_elements(Page::get_cobrand($q), 'contactForm', $q);
- my %vars = (
- header => $header,
- errors => $errors,
- intro => $intro,
- item_title => $item_title,
- item_meta => $item_meta,
- item_body => $item_body,
- hidden_vals => $hidden_vals,
- form_action => $form_action,
- input_h => \%input_h,
- field_errors => \%field_errors,
- label_name => _('Your name:'),
- label_email => _('Your&nbsp;email:'),
- label_subject => _('Subject:'),
- label_message => _('Message:'),
- label_submit => _('Post'),
- contact_details => contact_details($q),
- cobrand_form_elements => $cobrand_form_elements
- );
- $out .= Page::template_include('contact', $q, Page::template_root($q), %vars);
- return $out;
-}
-
diff --git a/web/css/cobrands/.gitignore b/web/css/cobrands/.gitignore
deleted file mode 100644
index 911c95650..000000000
--- a/web/css/cobrands/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/cities \ No newline at end of file
diff --git a/web/css/core.css b/web/css/core.css
index b31e6bc0a..747b76c47 100644
--- a/web/css/core.css
+++ b/web/css/core.css
@@ -13,7 +13,7 @@
color: #666666;
background-color: #cccccc;
}
-#mysociety p.error {
+#mysociety p.dev-site-notice, #mysociety p.error {
text-align: center;
color: #cc0000;
font-size: larger;
diff --git a/web/css/core.scss b/web/css/core.scss
index 8bbebfce1..99d9584e9 100644
--- a/web/css/core.scss
+++ b/web/css/core.scss
@@ -29,7 +29,7 @@ $map_width: 500px;
background-color: #cccccc;
}
- p.error {
+ p.dev-site-notice, p.error {
text-align: center;
color: #cc0000;
font-size: larger;
diff --git a/web/css/scambs.css b/web/css/scambs.css
deleted file mode 100644
index 38733d294..000000000
--- a/web/css/scambs.css
+++ /dev/null
@@ -1,1110 +0,0 @@
-/************************************************/
-/* scambs_web.css */
-/* Use for website and ActiveEdit */
-/************************************************/
-
-/***********************************************/
-/* HTML tag styles */
-/***********************************************/
-
-html {
- margin: 0px 10px;
- background: #E3E3CB;
- border-left: #999 solid 1px;
- border-right: #999 solid 1px;
-}
-/*added an extra pixel to make sure the background lines are seen in all browsers, especialy Opera*/
-
-body {
- margin: 0px;
- color: #333;
- padding: 0px;
- min-height: 100%;
- background: #fff;
- font-family: Verdana, Arial, Helvetica, sans-serif;
-}
-
-* html body {
- margin: 0px 10px;
- border-left: #999 solid 1px;
- border-right: #999 solid 1px;
- height: 100%;
-}
-
-a {
- color: #336699;
- text-decoration: none;
-}
-
-a:link{
- color: #336699;
- text-decoration: none;
-}
-
-a:visited{
- color: #336699;
- text-decoration: none;
-}
-
-a:hover{
- color: #666;
- text-decoration: underline;
-}
-
-a:active, a:focus {
- color: #fff;
- background: #660000;
-}
-
-form {
- margin: 0px;
-}
-
-h1 {
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 1em;
- color: #003366;
- text-transform: uppercase;
- font-weight: bold;
- letter-spacing: 0.1em;
-}
-
-h2{
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 1em;
- line-height: 1.2em;
- font-weight: bold;
- color: #006699;
-}
-
-h3{
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 1em;
- line-height: 1.1em;
- color: #003366;
-}
-
-h4{
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 1em;
- line-height: 1em;
- color: #000000;
-}
-
-ul li {
- list-style-type: disc;
- font-size: 1em;
-}
-
-
-ol li {
- list-style-type: decimal;
-}
-
-img {
- border: 0px;
-}
-
-th {
- color: #000000;
- text-align: left;
-}
-
-/* Structured Pages Styles */
-
-#menupanel span.year {
- font-size: 1.2em;
-}
-
-#centrepanel table.listings {
- border: 1px solid #006699;
- width: 100%;
-}
-
-#centrepanel th {
- color: #003366;
- text-align: left;
- border-bottom: 1px solid #006699;
-}
-
-#centrepanel th.day {
- width: 40px;
-}
-
-#centrepanel th.date {
- width: 100px;
-}
-
-#centrepanel th.type {
- width: 60px;
-}
-
-.lefttop
-{
- text-align: left;
- vertical-align:top;
-}
-
-/*** Search Result Styles***/
-
-table.searchresults td {
- text-align: left;
- vertical-align: top;
-}
-
-tr.resultrow td {
- background: #eee;
-}
-
-tr.result td {
- padding-bottom: 20px;
-}
-
-span.resultdate {
- color: #006699;
- font-style: italic;
-}
-
-/***********************************************/
-/* Layout Divs */
-/***********************************************/
-/* Skip content links*/
-a.skip {
- float: left;
- position: absolute; left: -500em; width: 20em;
-}
-
-.test {
- background: url(http://www.scambs.gov.uk/system/images/scambs/bgdots.gif) repeat-y 180px 0px;
- min-height: 1000px;
- height: 100%;
- clear: both;
- }
-
-/*allcontent*/
-#content{
- background: url(http://www.scambs.gov.uk/system/images/scambs/bgdots_right.gif) repeat-y top right;
- width: 100%;
- height: 100%;
- margin: 0px;
- clear: both;
-}
-
-#contentnodots{
- width: 100%;
- height: 100%;
- margin: 0px;
- clear: both;
-}
-
-/*top layer*/
-#top {
- clear: both;
- height: 1.7em;
- padding: 0px 0px 0px 0px;
- border-bottom: 1px solid #fff;
- background: #013B63 url(http://www.scambs.gov.uk/system/images/scambs/topbck.jpg) repeat-y;
-}
-
-#topnav {
- padding: 4px 10px 0px 15px;
- font-family: "trebuchet MS", Helvetica, sans-serif;
- font-weight: bold;
- font-size: 0.7em;
- text-align: right;
- color: #fff;
-}
-
-#topnav a:link {
- color: #fff;
-}
-
-#topnav a:visited {
- color: #fff;
-}
-#topnav a:hover {
- color: #CCCCCC;
-}
-
-#topnav a:active, #topnav a:focus {
- color: #fff;
-}
-
-#topnav ul, #topnav li {
- display: inline;
- margin: 0px;
- }
-
-/***********************************************/
-
-.floatleft {
- float: left;
-}
-
-.floatright {
- float: right;
-}
-
-.block {
- display: block;
-}
-
-.clear {
- clear: both;
-}
-
-/*use for hidden text */
-.hide {
- display: none;
-}
-
-.uline {
- text-decoration: underline;
-}
-
-.italic {
- text-decoration: italic;
-}
-
-.dotted {
-background: url(http://www.scambs.gov.uk/system/images/scambs/bgdots.gif) repeat-x left center;
-height: 1px;
-}
-
-.dottedgreyback {
-background: #f7f7f7 url(http://www.scambs.gov.uk/system/images/scambs/bgdots.gif) repeat-x left center;
-height: 1px;
-}
-
-.greyback {
-background: #f7f7f7;
-}
-
-.dkgreyback {
-background: #efefef;
-}
-
-.blueback {
-background: #e0f1ff;
-}
-
-.pinkback {
-background: #ffe0e6;
-}
-
-.yellowback {
-background: #ffffd0;
-}
-
-.blackback {
-background: #000000;
-}
-
-.purpleback {
-background: #813f62;
-}
-
-.greenback {
-background: #008000;
-}
-
-.redtext {
-color: red;
-}
-
-.whitetext {
-color: white;
-}
-
-.bluetext {
-color: #006699;
-}
-
-.centertext {
-text-align: center
-}
-
-.AZ {
- padding-right: 2px;
- padding-left: 2px;
- border: 1px solid gray;
- font-weight: bold;
- float: left;
- margin: 1px;
- width: 10px;
- text-align: center;
-}
-
-.AZselected {
- padding-right: 2px;
- padding-left: 2px;
- border: 1px solid gray;
- font-weight: bold;
- float: left;
- margin: 1px;
- text-decoration: underline;
-}
-
-/*job vacancies styles*/
-#jobvacs div {
- /*float: left;*/
- /*width: 240px;*/
- padding-right: 5px;
-}
-
-#jobvacs span {
- /*float: left;*/
-}
-
-#jobvacs div div {
- width: 100px;
- font-weight: bold;
- float: left;
-}
-
-#jobvacs br {
- /*clear: left;*/
-}
-
-/***********************************************/
-
-/*masthead*/
-#masthead {
- clear: both;
- display: block;
- height: 150px;
- width: 100%;
- border-bottom: 1px solid #fff;
- background: #013B63;
-}
-
-#bannerimage {
- height: 150px;
- background: url(http://www.scambs.gov.uk/images/southcambridgeshiredistrictcouncil/headers/defaultBg.jpg) no-repeat top left;
-}
-
-#Housingbannerimage {
- height: 150px;
- background: url(http://www.scambs.gov.uk/images/southcambridgeshiredistrictcouncil/headers/housing.jpg) no-repeat top left;
-}
-
-#homeimage {
- display: block;
- float:left;
- height: 150px;
- width:420px;
- margin: 0;
-}
-
-#homeimage img {
- display: inline;
- float:left;
-}
-
-#logo{
- display: block;
- float:right;
- height: 150px;
- margin: 0px;
- border-left: 2px solid #fff;
-}
-
-/***********************************************/
-
-/*header layer*/
-#header {
- clear: both;
- display: block;
- height: 1.9em;
- background: #427499 url(http://www.scambs.gov.uk/system/images/scambs/headerbck.jpg) repeat-y;
-}
-
-/*Header navigation*/
-#headernav {
- display: block;
- float: left;
- padding: 7px 10px 0px 15px;
- font-family: "trebuchet MS", Helvetica, sans-serif;
- font-weight: bold;
- font-size: 0.8em;
- text-align: left;
- color: #fff;
-}
-
-#headernav a:link {
- color: #fff;
-}
-
-#headernav a:visited {
- color: #fff;
-}
-#headernav a:hover {
- color: #CCCCCC;
-}
-
-#headernav a:active, #headernav a:focus {
- color: #fff;
-}
-
-/*Search navigation*/
-#headersearch {
- padding: 2px 10px 0px 5px;
- display: block;
- float: right;
- width: 300px;
- height: 27px;
- font-family: "trebuchet MS", Helvetica, sans-serif;
- font-weight: bold;
- font-size: 0.7em;
- text-align: right;
- color: #fff;
-}
-
-#headersearch a:link {
- color: #fff;
-}
-
-#headersearch a:visited {
- color: #fff;
-}
-#headersearch a:hover {
- color: #CCCCCC;
-}
-
-#headersearch a:active, #headersearch a:focus {
- color: #fff;
-}
-
-.form {
- padding: 0px 5px 0px 5px;
-}
-
-/***********************************************/
-
-
-/*Breadcrumb layer*/
-#breadcrumb {
- clear: both;
- width: 100%;
- height: 1.9em;
- margin: 0px;
- background: url(http://www.scambs.gov.uk/system/images/scambs/breadcrumb.jpg) repeat-y;
-}
-
-/*Breadcrumb navigation*/
-#breadcrumbtext {
- display: block;
- float: left;
- padding: 7px 10px 0px 15px;
- font-family: "trebuchet MS", Helvetica, sans-serif;
- font-weight: bold;
- font-size: 0.7em;
- text-align: left;
- color: #006699;
-}
-
-#breadcrumbtext a:link {
- color: #003366;
-}
-
-#breadcrumbtext a:visited {
- color: #003366;
-}
-
-#breadcrumbtext a:hover {
- color: #333;
-}
-
-#breadcrumbtext a:active, #breadcrumbtext a:focus {
- color: #fff;
-}
-
-/*************************/
-/*Content layer*/
-/* mySociety commented out
-#maintext img {
- border: 1px solid #999;
- padding: 2px;
- margin: 0px;
-}
-*/
-
-#maintext {
- display: block;
- margin: 20px 0px 0px 0px;
- padding: 5px 15px;
- font-family: Verdana, Arial, Helvetica, sans-serif;
- line-height: 1.4em;
- font-weight: normal;
- color: #333;
- height: 100%;
-}
-
-#maintext h1 {
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 1em;
- color: #003366;
- text-transform: uppercase;
- font-weight: bold;
- letter-spacing: 0.1em;
-}
-
-/*Use for subheadings*/
-#maintext h2{
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 1em;
- line-height: 1.2em;
- font-weight: bold;
- color: #006699;
-}
-
-/*Use for textheadings*/
-#maintext h3{
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 0.9em;
- line-height: 1em;
- color: #003366;
-}
-
-/* mySociety commented out
-#maintext ul, #maintext ol {
- margin: 0;
- padding: 0px 0px 0px 15px;
- border: none;
-}
-*/
-
-/* Style for unordered list*/
-/* mySociety commented out
-#maintext ul li {
- font-family: Verdana, Arial, Helvetica, sans-serif;
- color: #333;
- line-height: 1.4em;
- padding: 0;
- list-style-type: disc;
-}
-*/
-
-/* Style for ordered list*/
-/* mySociety commented out
-#maintext ol li {
- font-family: Verdana, Arial, Helvetica, sans-serif;
- color: #333;
- line-height: 1.4em;
- padding: 0;
- list-style-type: decimal;
-}
-*/
-
-#formtable img {
- border: 0px;
- padding: 5px;
-}
-
-#maintext ul.hlist, #maintext ul.hlist li {
- display: block;
- list-style: none;
- margin: 0;
- padding: 0;
-}
-
-#maintext ul.hlist li {
- float: left;
-}
-
-#maintext ul.eventdate-year, #maintext ul.eventdate-month {
- height: 30px;
-}
-
-#maintext ul.eventdate-year {
- font-size: 1em;
-}
-
-#maintext ul.eventdate-year li, #maintext ul.eventdate-month li {
- margin: 0 5px 0 0;
-}
-
-#maintext ul.eventdate-year li strong, #maintext ul.eventdate-month li strong {
- margin: 0 0 0 5px;
-}
-
-#maintext ul.eventdate-year li a, #maintext ul.eventdate-month li a {
- margin: 0 0 0 5px;
-}
-
-#maintext ul.eventdate-year li a strong, #maintext ul.eventdate-month li a strong {
- margin: 0;
-}
-
-/***********************************************/
-
-/*Content Left Column layer*/
-#left{
- float: left;
- width: 183px;
- height: 100%;
- background: url(http://www.scambs.gov.uk/system/images/scambs/bgdots.gif) repeat-y right top;
-}
-
-#sectiontitle {
- display: block;
- height: 25px;
- background: #003366;
- margin-right: 1px;
-}
-
-#sectiontitle ul{
- font-family: "trebuchet MS", Helvetica, sans-serif;
- font-weight: normal;
- font-size: 0.8em;
- color: #fff;
- padding: 5px 0px 0px 15px;
- margin: 0px;
-}
-
-#sectiontitle ul li {
- list-style-type: none;
-}
-
-#sectiontitle a:link {
- color: #fff;
- text-decoration: none;
-}
-
-#sectiontitle a:visited {
- color: #fff;
- text-decoration: none;
-}
-
-#sectiontitle a:hover {
- color: #cccccc;
- text-decoration: underline;
-}
-
-#sectiontitle a:active, #sectiontitle a:focus {
- color: #fff;
-}
-
-#leftnav ul {
- margin: 0;
- padding: 0;
- border: none;
- display: block;
- clear: both;
- background: url(http://www.scambs.gov.uk/system/images/scambs/bgdots.gif) repeat-x left bottom;
-}
-
-#leftnav ul li {
- list-style-type: none;
-}
-
-/*First level headings*/
-#leftnav ul li.first{
- padding: 6px 5px 10px 15px;
- font-family: "trebuchet MS", Helvetica, sans-serif;
- font-weight: bold;
- font-size: 0.8em;
- color: #003366;
- background-image: url(http://www.scambs.gov.uk/system/images/scambs/menu1_arrow.gif);
- background-repeat: no-repeat;
-}
-
-#leftnav ul li a:link {
- color: #003366;
-}
-
-#leftnav ul li a:visited {
- color: #003366;
-}
-
-#leftnav ul li a:hover {
- color: #666;
-}
-
-#leftnav ul li a:active, #leftnav ul li a:focus {
- color: #fff;
-}
-
-/*Second level headings*/
-#leftnav ul li ul.second{
- font-family: "trebuchet MS", Helvetica, sans-serif;
- font-weight: normal;
- line-height: 1.1em;
- text-align: left;
- margin: 0;
- padding: 0;
- border: none;
- color: #663300;
- background: url(http://www.scambs.gov.uk/system/images/scambs/blank.gif) repeat-x left bottom;
-}
-
-#leftnav ul li ul li
-{
- padding-right: 0px;
- padding-left: 15px;
- background-image: url(http://www.scambs.gov.uk/system/images/scambs/menu2_arrow.gif);
- padding-bottom: 0px;
- padding-top: 0px;
- background-repeat: no-repeat;
-}
-
-#leftnav ul li ul a:link {
- color: #663300;
-}
-
-#leftnav ul li ul a:visited {
- color: #663300;
-}
-
-#leftnav ul li ul a:hover {
- color: #666;
- text-decoration: underline;
-}
-
-#leftnav ul li ul a:active, #leftnav ul li ul a:focus {
- color: #fff;
-}
-
-/***********************************************/
-/*Content Middle Column layer*/
-
-#middle{
- padding: 0px 0px 30px 0px;
- /* margin: 0px 235px 0px 182px; mySociety changed */
- margin: 0 0 0 182px;
- height: 100%;
- background: #fff;
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 0.7em;
-}
-
-#middle .padding {
- padding: 0px 10px 10px 10px;
-}
-
-#middletop h1, #middleleft h1, #middleright h1 {
- font-family: Verdana, Arial, Helvetica, sans-serif;
- color: #003366;
- text-transform: uppercase;
- font-weight: bold;
- letter-spacing: 0.1em;
- font-size: 1em;
-}
-
-#middle a:link {
- color: #006699;
- text-decoration: none;
-}
-
-#middle a:visited {
- color: #006699;
- text-decoration: none;
-}
-
-#middle a:hover {
- color: #666;
- text-decoration: underline;
-}
-
-#middle a:active, #middle a:focus {
- color: #fff;
-}
-
-/*************************/
-/*Content Middle Top layer*/
-#middletop {
- padding-top: 22px;
- margin: 0px;
- height: 1px;
-}
-
-#middletop p {
- color: #000000;
- line-height: 1.2em;
-}
-
-#mtfeature1, #mtfeature2, #mtfeature3, #mtfeature4, .mtfeature {
- display: block;
-}
-
-#mtimage1, #mtimage2, #mtimage3, #mtimage4, .mtimage {
- display:block;
- float: left;
-}
-
-#mtimage1 img, #mtimage2 img, #mtimage3, #mtimage4 img, .mtimage img{
- border: 1px solid #999;
- padding: 2px;
- margin: 0px 10px 0px 0px;
-}
-
-#mttext1, #mttext2, #mttext3, #mttext4, .mttext {
- display: block;
-}
-
-
-#middletop h2{
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 1em;
- line-height: 1em;
- font-weight:bold;
- color: #003366;
- margin: 0px;
-}
-
-
-/*************************/
-/*Content Middle left layer*/
-
-#middleleft a:link, #middleright a:link, #middlebottom a:link {
- text-decoration: none;
-}
-
-#middleleft {
- width: 49%;
- float: left;
- padding: 10px;
- margin: 0px;
- margin-right: -10px;
- height: 1%;
-}
-
-#middleleft ul {
- margin: 0;
- padding: 0px 0px 0px 15px;
- border: none;
- clear: both;
-}
-
-#middleleft ul li{
- color: #000000;
- line-height: 1.5em;
- padding: 0px 10px 0px 0px;
- list-style-image: url(http://www.scambs.gov.uk/system/images/scambs/arrow2.gif) ;
-}
-
-
-/*************************/
-/*Content Middle right layer*/
-
-#middleright {
- margin-left: 49%;
- background: url(http://www.scambs.gov.uk/system/images/scambs/bgdots.gif) repeat-y left top;
- padding: 10px;
- height: 1%;
-}
-
-#middleright ul {
- margin: 0;
- padding: 0;
- border: none;
-}
-
-#middleright ul li{
- padding: 0;
- margin: 0;
- list-style: none;
-}
-
-
-/*************************/
-/*Content Middle bottom layer*/
-
-#middlebottom, .middlebottom {
- margin-top: 10px;
- padding: 2px;
- background: url(http://www.scambs.gov.uk/system/images/scambs/bgdots.gif) repeat-x;
- height: 1%;
-}
-
-/***********************************************/
-/*Content Right Column layer*/
-
-#right{
- float: right;
- width: 235px;
- background: #fff url(http://www.scambs.gov.uk/system/images/scambs/bgdots.gif) repeat-y left top;
- height: 100%;
- margin: 0px;
-}
-
-.rightcontent{
- padding: 0px 3px 10px 8px;
-}
-
-.rightcontent h1 {
- color: #663300;
- font-weight: bold;
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 0.7em;
-}
-
-#right .title {
- font-family: "trebuchet MS", Helvetica, sans-serif;
- font-size: 1em;
- color: #663300;
- text-transform: uppercase;
- font-weight: bold;
- letter-spacing: 0.1em;
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 0.7em;
- line-height: 0.8em;
-}
-
-.rightcontent ul {
- margin: 0px;
- padding: 0px 0px 0px 17px;
- border: none;
-}
-
-.rightcontent ul li {
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 0.7em;
- line-height: 1.5em;
- padding: 0px;
- list-style-image: url(http://www.scambs.gov.uk/system/images/scambs/arrow2.gif) ;
-}
-
-/**Right Content 1**/
-#rightcontent1, #address, div.address {
- padding: 5px 3px 10px 8px;
- background: url(http://www.scambs.gov.uk/system/images/scambs/bgdots.gif) repeat-x left bottom;
-}
-
-#rightcontent1 h1, #address h1, div.address h1 {
- font-family: "trebuchet MS", Helvetica, sans-serif;
- font-size: 1em;
- color: #663300;
- text-transform: uppercase;
- font-weight: bold;
- letter-spacing: 0.1em;
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 0.7em;
- line-height: 0.8em;
-}
-
-#rightcontent1 h2, #address h2, div.address h2{
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 0.7em;
- color: #333;
- margin: 0px;
- padding: 0px;
- line-height: 0.8em;
-}
-
-
-#rightcontent1 p, #address p, div.address p {
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 0.7em;
- color: #000000;
- line-height: 1.2em;
-}
-
-
-/**Right Content 2**/
-#rightcontent2 {
- padding: 0px 3px 10px 8px;
- background: url(http://www.scambs.gov.uk/system/images/scambs/bgdots.gif) repeat-x left bottom;
-}
-#rightcontent2 h1{ font-family: "trebuchet MS", Helvetica, sans-serif;
- color: #663300;
- font-weight: bold;
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 0.7em;
- line-height: 1em;
-}
-
-#rightcontent2 ul, #rightcontent3 ul {
- margin: 0px;
- padding: 0px 0px 0px 17px;
- border: none;
-}
-
-#rightcontent2 ul li, #rightcontent3 ul li {
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 0.7em;
- line-height: 1.5em;
- padding: 0px;
- list-style-image: url(http://www.scambs.gov.uk/system/images/scambs/arrow2.gif) ;
-}
-
-/**Right Content 3**/
-#rightcontent3 {
- padding: 0px 3px 10px 8px;
-}
-#rightcontent3 h1{
- color: #663300;
- font-weight: bold;
- font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 0.7em;
-}
-
-#bottominfo {
- margin: -40px 235px 0px 200px;
- display: block;
- clear: both;
- height: 30px;
- text-align: right;
- font-size: 0.7em;
- padding: 5px;
-}
-
-#bottominfo img {
- width: 15px;
- height: 15px;
- margin-left: 5px;
-}
-
-#bottominfo ul {
- margin: 0px;
- padding: 2px;
-}
-
-#bottominfo ul li {
- list-style-type: none;
- display: inline;
-}
-
-/***********************************************/
-/*Footer layer*/
-#footer{
- display: block;
- width: 100%;
- height: 30px;
- background: #427499 url(http://www.scambs.gov.uk/system/images/scambs/headerbck.jpg) no-repeat;
-}
-
-#footertext{
- padding: 5px 0px 0px 15px;
- font-family: "trebuchet MS", Helvetica, sans-serif;
- font-weight: bold;
- font-size: 0.7em;
- text-align: left;
- color: #fff;
-}
-
-#footertext a:link {
- color: #fff;
-}
-
-#footertext a:visited {
- color: #fff;
-}
-#footertext a:hover {
- color: #CCCCCC;
-}
-
-#footertext a:active, #footertext a:focus {
- color: #fff;
-}
-
-#centrepanel img {
- border: 0px;
- padding: 0px;
-}
-
-/***********************************************/
-
-.aligncentre{
- text-align: center;
-}
diff --git a/web/faq.cgi b/web/faq.cgi
deleted file mode 100755
index 4d9c7413a..000000000
--- a/web/faq.cgi
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# faq.cgi:
-# FAQ page for FixMyStreet
-#
-# Copyright (c) 2006 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: faq.cgi,v 1.42 2009-07-10 16:10:22 matthew Exp $
-
-use strict;
-use Standard -db;
-use mySociety::Locale;
-
-my $lastmodified = (stat $0)[9];
-sub main {
- my $q = shift;
- print Page::header($q, title=>_('Frequently Asked Questions'));
- my $lang = $mySociety::Locale::lang;
- print Page::template_include("faq-$lang", $q, Page::template_root($q));
- print Page::footer($q);
-}
-Page::do_fastcgi(\&main, $lastmodified);
-
diff --git a/web/fixmystreet_app_cgi.cgi b/web/fixmystreet_app_cgi.cgi
new file mode 100755
index 000000000..7d60ce673
--- /dev/null
+++ b/web/fixmystreet_app_cgi.cgi
@@ -0,0 +1,35 @@
+#!/usr/bin/env perl
+
+BEGIN { # set all the paths to the perl code
+ use FindBin;
+ require "$FindBin::Bin/../setenv.pl";
+}
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run( 'FixMyStreet::App', 'CGI' );
+
+1;
+
+=head1 NAME
+
+fixmystreet_app_cgi.pl - Catalyst CGI
+
+=head1 SYNOPSIS
+
+See L<Catalyst::Manual>
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as a cgi script.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
diff --git a/web/fixmystreet_app_fastcgi.cgi b/web/fixmystreet_app_fastcgi.cgi
new file mode 100755
index 000000000..1059cbd34
--- /dev/null
+++ b/web/fixmystreet_app_fastcgi.cgi
@@ -0,0 +1,53 @@
+#!/usr/bin/env perl
+
+BEGIN { # set all the paths to the perl code
+ use FindBin;
+ require "$FindBin::Bin/../setenv.pl";
+}
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run( 'FixMyStreet::App', 'FastCGI' );
+
+1;
+
+=head1 NAME
+
+fixmystreet_app_fastcgi.pl - Catalyst FastCGI
+
+=head1 SYNOPSIS
+
+fixmystreet_app_fastcgi.pl [options]
+
+ Options:
+ -? -help display this help and exits
+ -l --listen Socket path to listen on
+ (defaults to standard input)
+ can be HOST:PORT, :PORT or a
+ filesystem path
+ -n --nproc specify number of processes to keep
+ to serve requests (defaults to 1,
+ requires -listen)
+ -p --pidfile specify filename for pid file
+ (requires -listen)
+ -d --daemon daemonize (requires -listen)
+ -M --manager specify alternate process manager
+ (FCGI::ProcManager sub-class)
+ or empty string to disable
+ -e --keeperr send error messages to STDOUT, not
+ to the webserver
+ --proc_title Set the process title (is possible)
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as fastcgi.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
diff --git a/web/flickr.cgi b/web/flickr.cgi
deleted file mode 100755
index 0ccce316c..000000000
--- a/web/flickr.cgi
+++ /dev/null
@@ -1,101 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# flickr.cgi:
-# Register for Flickr usage, and update photos
-#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: flickr.cgi,v 1.9 2008-10-09 14:20:54 matthew Exp $
-
-use strict;
-use Standard;
-use LWP::Simple;
-use URI::Escape;
-use mySociety::AuthToken;
-use mySociety::Email;
-use mySociety::EmailUtil;
-use mySociety::Random qw(random_bytes);
-
-sub main {
- my $q = shift;
- print Page::header($q, title=>'Flickr photo upload');
- my $out = '';
- if (my $token = $q->param('token')) {
- my $email = mySociety::AuthToken::retrieve('flickr', $token);
- if ($email) {
- my $key = mySociety::Config::get('FLICKR_API');
- my $url = 'http://api.flickr.com/services/rest/?method=flickr.people.findByEmail&api_key='.$key.'&find_email=' . uri_escape($email);
- my $result = get($url);
- my ($nsid) = $result =~ /nsid="([^"]*)"/;
- $url = 'http://api.flickr.com/services/rest/?method=flickr.people.getInfo&api_key='.$key.'&user_id=' . uri_escape($nsid);
- $result = get($url);
- my ($name) = $result =~ /<realname>(.*?)<\/realname>/;
- $name ||= '';
-
- my $id = dbh()->selectrow_array("select nextval('partial_user_id_seq');");
- dbh()->do("insert into partial_user (id, service, nsid, name, email, phone) values (?, 'flickr', ?, ?, ?, '')", {},
- $id, $nsid, $name, $email);
- dbh()->commit();
- $out .= $q->p('Thanks for confirming your email address. Please now tag
-your photos with FixMyStreet (and geo-tag them if you want/can, automatically if possible!)
-for us to pick them up.');
- } else {
- $out = $q->p(_(<<EOF));
-Thank you for trying to register for your Flickr photos. We seem to have a
-problem ourselves though, so <a href="/contact">please let us know what went on</a>
-and we'll look into it.
-EOF
- }
- } elsif (my $email = $q->param('email')) {
- my $template = File::Slurp::read_file("$FindBin::Bin/../templates/emails/flickr-confirm");
- my %h = ();
- my $token = mySociety::AuthToken::store('flickr', $email);
- $h{url} = mySociety::Config::get('BASE_URL') . '/F/' . $token;
-
- my $body = mySociety::Email::construct_email({
- _template_ => $template,
- _parameters_ => \%h,
- To => $email,
- From => [ mySociety::Config::get('CONTACT_EMAIL'), 'FixMyStreet' ],
- 'Message-ID' => sprintf('<flickr-%s-%s@mysociety.org>', time(), unpack('h*', random_bytes(5, 1))),
- });
-
- my $result;
- $result = mySociety::EmailUtil::send_email($body, mySociety::Config::get('CONTACT_EMAIL'), $email);
- if ($result == mySociety::EmailUtil::EMAIL_SUCCESS) {
- $out = 'Thanks, we\'ve sent you a confirmation email!';
- dbh()->commit();
- } else {
- $out = 'Sorry, something went wrong - very alpha!';
- dbh()->rollback();
- }
- } else {
- $out .= <<EOF;
-<p><strong>This feature was added for HackDay London 2007, and might not be of production quality.</strong>
-Please <a href="/contact">send bug reports to us</a>.</p>
-<p>Using the Flickr API, FixMyStreet can utilise all the methods of uploading photos to Flickr
-to report problems to your council:</p>
-<ol>
-<li>Register that you're going to be using Flickr here, so we know to check your photos.
-<li>Upload your photo to Flickr, for example via camera phone on location
-<li>Tag the photo with FixMyStreet when uploading, or afterwards
-<li>Locate the problem on Flickr's map (if you have GPS, this might be done automatically :) )
-<li>FixMyStreet will find the photo, and ask you to add/ check the details;
-<li>The report is then sent to the council.
-</ol>
-
-<form method="post">
-<p>To begin, please enter your Flickr email address, both so we know the account to watch and
-so we can email you when you upload FixMyStreet photos with a link to check and confirm
-the details: <input type="text" name="email" value="" size="30">
-<input type="submit" value="Go">
-</p></form>
-EOF
- }
-
- print $out;
- print Page::footer($q);
-}
-Page::do_fastcgi(\&main);
-
diff --git a/web/fun.cgi b/web/fun.cgi
deleted file mode 100755
index d93658dff..000000000
--- a/web/fun.cgi
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# fun.cgi:
-# Weird and Wonderful
-#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: fun.cgi,v 1.3 2008-09-19 10:24:55 matthew Exp $
-
-use strict;
-use Standard -db;
-
-# Main code for index.cgi
-sub main {
- my $q = shift;
- print Page::header($q, title=>_('Weird and Wonderful reports'));
- print fun($q);
- print Page::footer($q);
-}
-Page::do_fastcgi(\&main);
-
-sub fun {
- my $q = shift;
- my $out = $q->h1(_('Weird and Wonderful reports'));
- $out .= $q->p('Here are some of the best or strangest reports we&rsquo;ve seen on FixMyStreet.
-They&rsquo;ve all been fixed, and in one case could have saved lives!
-Do let us know if you find any more.');
- $out .= $q->ul({style => 'list-style-type: none; margin:0; padding:0'},
- $q->li(
- $q->img({src=>'http://www.fixmystreet.com/photo?id=9468', align=>'right', hspace=>8}),
- $q->h2('Dumped Piano (right)'),
- $q->p('The reporter of this problem summed it up with their report,
-which consisted solely of the one character &ldquo;!&rdquo;. &mdash;',
-$q->a({href=>'http://www.fixmystreet.com/report/9468'}, 'Problem report')),
- ),
- $q->li(
- $q->h2('Mad Seagull'),
- $q->p('&ldquo;A seagull is attacking various cars within this road. He starts at around 05:45 every morning and continues until around 19:30. This causes a lot of noisy banging and wakes up children.&rdquo; &mdash;',
-$q->a({href=>'http://www.fixmystreet.com/report/2722'}, 'Problem report')),
- ),
- $q->li(
- $q->img({src=>'http://www.fixmystreet.com/photo?id=6553', align=>'right', hspace=>8}),
- $q->h2('Boxes full of cheese dumped (right)'),
- $q->p('&ldquo;About a dozen boxes full of mozzarella cheese have been dumped opposite 3 rufford street. if it warms up we could have nasty road topping problem (seriously there is a lot of cheese)&rdquo; &mdash;',
-$q->a({href=>'http://www.fixmystreet.com/report/6553'}, 'Problem report')),
- ),
- $q->li(
- $q->h2('Dangerous Nivea Billboard'),
- $q->p('&ldquo;The Nivea \'Oxygen is a wonderful thing\' billboard here has a device on it releasing bubbles and foam. This is blowing into the road which is both distracting and dangerous to drivers. A large ball of foam hit my windscreen unexpectedly and nearly caused me to have an accident&rdquo; &mdash;',
-$q->a({href=>'http://www.fixmystreet.com/report/7552'}, 'Problem report')),
- ),
- );
- return $out;
-}
-
diff --git a/web/import.cgi b/web/import.cgi
deleted file mode 100755
index 76ac61841..000000000
--- a/web/import.cgi
+++ /dev/null
@@ -1,173 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# import.cgi
-# Script to which things like iPhones can POST new data
-#
-# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: import.cgi,v 1.11 2009-12-10 16:22:49 matthew Exp $
-
-use strict;
-use Error qw(:try);
-use Standard;
-use Utils;
-use mySociety::AuthToken;
-use mySociety::Config;
-use mySociety::EmailUtil;
-
-sub main {
- my $q = shift;
-
- my @vars = qw(service subject detail name email phone easting northing lat lon id phone_id);
- my %input = map { $_ => $q->param($_) || '' } @vars;
- my @errors;
-
- unless ($ENV{REQUEST_METHOD} eq 'POST') {
- print Page::header($q, title=>'External import');
- docs();
- print Page::footer($q);
- return;
- }
-
- # If we were given easting, northing convert to lat lon now
- my $latitude = $input{lat} ||= 0;
- my $longitude = $input{lon} ||= 0;
- if (
- !( $latitude || $longitude ) # have not been given lat or lon
- && ( $input{easting} && $input{northing} ) # but do have e and n
- )
- {
- ( $latitude, $longitude ) =
- Utils::convert_en_to_latlon( $input{easting}, $input{northing});
- }
-
- my $fh = $q->upload('photo'); # MUST come before $q->header, don't know why!
- print $q->header(-charset => 'utf-8', -content_type => 'text/plain');
-
- if ($fh) {
- my $err = Page::check_photo($q, $fh);
- push @errors, $err if $err;
- }
-
- push @errors, 'You must supply a service' unless $input{service};
- push @errors, 'Please enter a subject' unless $input{subject} && $input{subject} =~ /\S/;
- push @errors, 'Please enter your name' unless $input{name} && $input{name} =~ /\S/;
-
- if (!$input{email} || $input{email} !~ /\S/) {
- push @errors, 'Please enter your email';
- } elsif (!mySociety::EmailUtil::is_valid_email($input{email})) {
- push @errors, 'Please enter a valid email';
- }
-
- if ( $latitude && mySociety::Config::get('COUNTRY') eq 'GB' ) {
- try {
- Utils::convert_latlon_to_en( $latitude, $longitude );
- } catch Error::Simple with {
- my $e = shift;
- push @errors, "We had a problem with the supplied co-ordinates - outside the UK?";
- };
- }
-
- # TODO: Get location from photo if present in EXIF data?
-
- my $photo;
- if ($fh) {
- try {
- $photo = Page::process_photo($fh, 1);
- } catch Error::Simple with {
- my $e = shift;
- push @errors, "That photo doesn't appear to have uploaded correctly ($e), please try again.";
- };
- }
-
- unless ( $photo || ( $latitude || $longitude ) ) {
- push @errors, 'Either a location or a photo must be provided.';
- }
-
- if (@errors) {
- print map { "ERROR:$_\n" } @errors;
- return;
- }
-
- # Store for possible future use
- if ($input{id} || $input{phone_id}) {
- my $id = $input{id} || $input{phone_id};
- my $already = dbh()->selectrow_array('select id from partial_user where service=? and nsid=?', {}, $input{service}, $id);
- unless ($already) {
- dbh()->do('insert into partial_user (service, nsid, name, email, phone) values (?, ?, ?, ?, ?)',
- {}, $input{service}, $id, $input{name}, $input{email}, $input{phone});
- }
- }
-
- # Store what we have so far in the database
- my $id = dbh()->selectrow_array("select nextval('problem_id_seq')");
- Utils::workaround_pg_bytea("insert into problem
- (id, postcode, latitude, longitude, title, detail, name, service,
- email, phone, photo, state, used_map, anonymous, category, areas)
- values
- (?, '', ?, ?, ?, ?, ?, ?, ?, ?, ?, 'partial', 't', 'f', '', '')", 10,
- $id, $latitude, $longitude, $input{subject},
- $input{detail}, $input{name}, $input{service}, $input{email}, $input{phone}, $photo);
-
- my $token = mySociety::AuthToken::store('partial', $id);
- my %h = (
- name => $input{name} ? ' ' . $input{name} : '',
- url => Page::base_url_with_lang($q, undef, 1) . '/L/' . $token,
- service => $input{service},
- title => $input{title},
- detail => $input{detail},
- );
-
- Page::send_email($q, $input{email}, $input{name}, 'partial', %h);
-
- dbh()->commit();
- print 'SUCCESS';
-}
-
-Page::do_fastcgi(\&main);
-
-sub docs {
- my $base = mySociety::Config::get('BASE_URL');
- print <<EOF;
-<p>You may inject problem reports into FixMyStreet programatically using this
-simple interface. Upon receipt, an email will be sent to the address given,
-with a link the user must click in order to check the details of their report,
-add any other information they wish, and then submit to the council.
-
-<p>This interface returns a plain text response; either <samp>SUCCESS</samp> if
-the report has been successfully received, or if not, a list of errors, one per
-line each starting with <samp>ERROR:</samp>.
-
-<p>You may submit the following information by POST to this URL
-(i.e. <samp>$base/import</samp> ):</p>
-<dl>
-<dt>service
-<dd>
-<em>Required</em>.
-Name of application/service using this interface.
-<dt>id
-<dd>Unique ID of a user/device, for possible future use.
-<br><small>(e.g. used by Flickr import to know which accounts to look at)</small>
-<dt>subject
-<dd>
-<em>Required</em>. Subject of problem report.
-<dt>detail
-<dd>Main body and details of problem report.
-<dt>name
-<dd>
-<em>Required</em>. Name of problem reporter.
-<dt>email
-<dd>
-<em>Required</em>. Email address of problem reporter.
-<dt>phone
-<dd>Telephone number of problem reporter.
-<dt>easting / northing
-<dt>lat / lon
-<dd>Location of problem report. You can either supply eastings/northings, or WGS84 latitude/longitude.
-<dt>photo
-<dd>Photo of problem (JPEG only).
-</dl>
-EOF
-}
-
diff --git a/web/index.cgi b/web/index.cgi
deleted file mode 100755
index ef4b3eee1..000000000
--- a/web/index.cgi
+++ /dev/null
@@ -1,1163 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-#
-# index.cgi:
-# Main code for FixMyStreet
-#
-# Copyright (c) 2006 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-
-use strict;
-use Standard;
-use Utils;
-use Encode;
-use Error qw(:try);
-use File::Slurp;
-use CGI::Carp;
-use POSIX qw(strcoll);
-use URI::Escape;
-
-# use Carp::Always;
-
-use CrossSell;
-use FixMyStreet::Geocode;
-use mySociety::AuthToken;
-use mySociety::Config;
-use mySociety::DBHandle qw(select_all);
-use mySociety::EmailUtil;
-use mySociety::Locale;
-use mySociety::MaPit;
-use mySociety::PostcodeUtil;
-use mySociety::Random;
-use mySociety::VotingArea;
-use mySociety::Web qw(ent NewURL);
-use Utils;
-
-sub debug (@) {
- return;
- my ( $format, @args ) = @_;
- warn sprintf $format, map { defined $_ ? $_ : 'undef' } @args;
-}
-
-BEGIN {
- if (!dbh()->selectrow_array('select secret from secret for update of secret')) {
- local dbh()->{HandleError};
- dbh()->do('insert into secret (secret) values (?)', {}, unpack('h*', mySociety::Random::random_bytes(32)));
- }
- dbh()->commit();
-}
-
-# Main code for index.cgi
-sub main {
- my $q = shift;
-
- if (my $partial = $q->param('partial_token')) {
- # We have a partial token, so fetch data from database and see where we're at.
- my $id = mySociety::AuthToken::retrieve('partial', $partial);
- if ($id) {
- my @row = dbh()->selectrow_array(
- "select latitude, longitude, name, email, title, (photo is not null) as has_photo, phone, detail
- from problem where id=? and state='partial'", {}, $id);
- if (@row) {
- $q->param('anonymous', 1);
- $q->param('submit_map', 1);
- $q->param('latitude', $row[0]);
- $q->param('longitude', $row[1]);
- $q->param('name', $row[2]);
- $q->param('email', $row[3]);
- $q->param('title', $row[4]);
- $q->param('has_photo', $row[5]);
- $q->param('phone', $row[6]);
- $q->param('detail', $row[7]);
- $q->param('partial', $partial);
- } else {
- my $base = mySociety::Config::get('BASE_URL');
- print $q->redirect(-location => $base . '/report/' . $id);
- }
- }
- }
-
- my $out = '';
- my %params;
- if ($q->param('submit_problem')) {
- $params{title} = _('Submitting your report');
- ($out, %params) = submit_problem($q);
- } elsif ($q->param('submit_update')) {
- $params{title} = _('Submitting your update');
- ($out, %params) = submit_update($q);
- } elsif ($q->param('submit_map')) {
- ($out, %params) = display_form($q, [], {});
- $params{title} = _('Reporting a problem');
- } elsif ($q->param('id')) {
- ($out, %params) = display_problem($q, [], {});
- $params{title} .= ' - ' . _('Viewing a problem');
- } elsif ($q->param('pc') || ($q->param('x') && $q->param('y')) || ($q->param('lat') || $q->param('lon'))) {
- ($out, %params) = display_location($q);
- $params{title} = _('Viewing a location');
- } elsif ($q->param('e') && $q->param('n')) {
- ($out, %params) = redirect_from_osgb_to_wgs84($q);
- } else {
- ($out, %params) = front_page($q);
- }
- print Page::header($q, %params);
- print $out;
- my %footerparams;
- $footerparams{js} = $params{js} if $params{js};
- $footerparams{template} = $params{template} if $params{template};
- print Page::footer($q, %footerparams);
-}
-Page::do_fastcgi(\&main);
-
-# Display front page
-sub front_page {
- my ($q, $error, $status_code) = @_;
- my $pc_h = ent($q->param('pc') || '');
-
- # Look up various cobrand things
- my $cobrand = Page::get_cobrand($q);
- my $cobrand_form_elements = Cobrand::form_elements($cobrand, 'postcodeForm', $q);
- my $form_action = Cobrand::url($cobrand, '/', $q);
- my $question = Cobrand::enter_postcode_text($cobrand, $q);
- $question = _("Enter a nearby GB postcode, or street name and area")
- unless $question;
- my %params = ('context' => 'front-page');
- $params{status_code} = $status_code if $status_code;
- my %vars = (
- error => $error || '',
- pc_h => $pc_h,
- cobrand_form_elements => $cobrand_form_elements,
- form_action => $form_action,
- question => "$question:",
- );
- my $cobrand_front_page = Page::template_include('front-page', $q, Page::template_root($q), %vars);
- return ($cobrand_front_page, %params) if $cobrand_front_page;
-
- my $out = '<p id="expl"><strong>' . _('Report, view, or discuss local problems') . '</strong>';
- my $subhead = _('(like graffiti, fly tipping, broken paving slabs, or street lighting)');
- $subhead = '(like graffiti, fly tipping, or broken paving slabs)'
- if $q->{site} eq 'southampton';
- $out .= '<br><small>' . $subhead . '</small>' if $subhead ne ' ';
- $out .= '</p>';
- #if (my $url = mySociety::Config::get('IPHONE_URL')) {
- # my $getiphone = _("Get FixMyStreet on your iPhone");
- # my $new = _("New!");
- # if ($q->{site} eq 'fixmystreet') {
- # $out .= <<EOF
-#<p align="center" style="margin-bottom:0">
-#<img width="23" height="12" alt="$new" src="/i/new.png" border="0">
-#<a href="$url">$getiphone</a>
-#</p>
-#EOF
- # }
- #}
- $out .= '<p class="error">' . $error . '</p>' if ($error);
-
- # Add pretty commas for display
- $out .= '<form action="' . $form_action . '" method="get" name="postcodeForm" id="postcodeForm">';
- if (my $token = $q->param('partial')) {
- my $id = mySociety::AuthToken::retrieve('partial', $token);
- if ($id) {
- my $thanks = _("Thanks for uploading your photo. We now need to locate your problem, so please enter a nearby street name or postcode in the box below&nbsp;:");
- $out .= <<EOF;
-<p style="margin-top: 0; color: #cc0000;"><img align="right" src="/photo?id=$id" hspace="5">$thanks</p>
-
-<input type="hidden" name="partial_token" value="$token">
-EOF
- }
- }
- my $activate = _("Go");
- $out .= <<EOF;
-<label for="pc">$question:</label>
-&nbsp;<input type="text" name="pc" value="$pc_h" id="pc" size="10" maxlength="200">
-&nbsp;<input type="submit" value="$activate" id="submit">
-$cobrand_form_elements
-</form>
-
-<div id="front_intro">
-EOF
- $out .= $q->h2(_('How to report a problem'));
- $out .= $q->ol(
- $q->li($question),
- $q->li(_('Locate the problem on a map of the area')),
- $q->li(_('Enter details of the problem')),
- $q->li(_('We send it to the council on your behalf'))
- );
-
-
- $out .= Cobrand::front_stats(Page::get_cobrand($q), $q);
-
- $out .= <<EOF;
-</div>
-
-EOF
-
- my $recent_photos = Cobrand::recent_photos(Page::get_cobrand($q), 3);
- my $probs = Cobrand::recent(Page::get_cobrand($q));
- if (@$probs || $recent_photos){
- $out .= '<div id="front_recent">';
- $out .= $q->h2(_('Photos of recent reports')) . $recent_photos if $recent_photos;
-
- $out .= $q->h2(_('Recently reported problems')) . ' <ul>' if @$probs;
- foreach (@$probs) {
- $out .= '<li><a href="/report/' . $_->{id} . '">'. ent($_->{title});
- $out .= '</a></li>';
- }
- $out .= '</ul>' if @$probs;
- $out .= '</div>';
- }
-
- if ($q->{site} eq 'emptyhomes') {
- $out .= <<EOF;
-<div id="eha_advert">
-Now is the best time to turn empty properties into empty homes... Don't miss it!
-<a href="http://www.emptyhomes.com/EHConference2011.html">Home Again: Empty Homes National Conference 2011</a>
-</div>
-EOF
- }
-
- return ($out, %params);
-}
-
-sub submit_update {
- my $q = shift;
- my @vars = qw(id name rznvy update fixed upload_fileid add_alert);
- my %input = map { $_ => $q->param($_) || '' } @vars;
- my @errors;
- my %field_errors;
-
- my $fh = $q->upload('photo');
- if ($fh) {
- my $err = Page::check_photo($q, $fh);
- push @errors, $err if $err;
- }
- $field_errors{update} = _('Please enter a message') unless $input{update} =~ /\S/;
- $input{name} = undef unless $input{name} =~ /\S/;
- if ($input{rznvy} !~ /\S/) {
- $field_errors{email} = _('Please enter your email');
- } elsif (!mySociety::EmailUtil::is_valid_email($input{rznvy})) {
- $field_errors{email} = _('Please enter a valid email');
- }
-
- my $image;
- if ($fh) {
- try {
- $image = Page::process_photo($fh);
- } catch Error::Simple with {
- my $e = shift;
- push(@errors, sprintf(_("That image doesn't appear to have uploaded correctly (%s), please try again."), $e));
- };
- }
-
- if ($input{upload_fileid}) {
- open FP, mySociety::Config::get('UPLOAD_CACHE') . $input{upload_fileid};
- $image = join('', <FP>);
- close FP;
- }
-
- return display_problem($q, \@errors, \%field_errors) if (@errors || scalar(keys(%field_errors)));
- my $cobrand = Page::get_cobrand($q);
- my $cobrand_data = Cobrand::extra_update_data($cobrand, $q);
- my $id = dbh()->selectrow_array("select nextval('comment_id_seq');");
- Utils::workaround_pg_bytea("insert into comment
- (id, problem_id, name, email, website, text, state, mark_fixed, photo, lang, cobrand, cobrand_data)
- values (?, ?, ?, ?, '', ?, 'unconfirmed', ?, ?, ?, ?, ?)", 7,
- $id, $input{id}, $input{name}, $input{rznvy}, $input{update},
- $input{fixed} ? 't' : 'f', $image, $mySociety::Locale::lang, $cobrand, $cobrand_data);
-
- my %h = ();
- $h{update} = $input{update};
- $h{name} = $input{name} ? $input{name} : _("Anonymous");
- my $base = Page::base_url_with_lang($q, undef, 1);
- $h{url} = $base . '/C/' . mySociety::AuthToken::store('update', { id => $id, add_alert => $input{add_alert} } );
- dbh()->commit();
-
- my $out = Page::send_confirmation_email($q, $input{rznvy}, $input{name}, 'update', %h);
- return $out;
-}
-
-sub submit_problem {
- my $q = shift;
- my @vars = qw(council title detail name email phone pc skipped anonymous category partial upload_fileid latitude longitude);
- my %input = map { $_ => scalar $q->param($_) } @vars;
- for (qw(title detail)) {
- $input{$_} = lc $input{$_} if $input{$_} !~ /[a-z]/;
- $input{$_} = ucfirst $input{$_};
- $input{$_} =~ s/\b(dog\s*)shit\b/$1poo/ig;
- $input{$_} =~ s/\b(porta)\s*([ck]abin|loo)\b/[$1ble $2]/ig;
- $input{$_} =~ s/kabin\]/cabin\]/ig;
- }
- my @errors;
- my %field_errors;
-
- my $cobrand = Page::get_cobrand($q);
-
- # If in UK and we have a lat,lon coocdinate check it is in UK
- if ( $input{latitude} && mySociety::Config::get('COUNTRY') eq 'GB' ) {
- try {
- Utils::convert_latlon_to_en( $input{latitude}, $input{longitude} );
- } catch Error::Simple with {
- my $e = shift;
- push @errors, "We had a problem with the supplied co-ordinates - outside the UK?";
- };
- }
-
- my $fh = $q->upload('photo');
- if ($fh) {
- my $err = Page::check_photo($q, $fh);
- $field_errors{photo} = $err if $err;
- }
-
- $input{council} = 2260 if $q->{site} eq 'scambs'; # All reports go to S. Cambs
- push(@errors, _('No council selected')) unless ($input{council} && $input{council} =~ /^(?:-1|[\d,]+(?:\|[\d,]+)?)$/);
- $field_errors{title} = _('Please enter a subject') unless $input{title} =~ /\S/;
- $field_errors{detail} = _('Please enter some details') unless $input{detail} =~ /\S/;
- if ($input{name} !~ /\S/) {
- $field_errors{name} = _('Please enter your name');
- } elsif (length($input{name}) < 5 || $input{name} !~ /\s/ || $input{name} =~ /\ba\s*n+on+((y|o)mo?u?s)?(ly)?\b/i) {
- $field_errors{name} = _('Please enter your full name, councils need this information - if you do not wish your name to be shown on the site, untick the box');
- }
- if ($input{email} !~ /\S/) {
- $field_errors{email} = _('Please enter your email');
- } elsif (!mySociety::EmailUtil::is_valid_email($input{email})) {
- $field_errors{email} = _('Please enter a valid email');
- }
- if ($input{category} && $input{category} eq _('-- Pick a category --')) {
- $field_errors{category} = _('Please choose a category');
- $input{category} = '';
- } elsif ($input{category} && $input{category} eq _('-- Pick a property type --')) {
- $field_errors{category} = _('Please choose a property type');
- $input{category} = '';
- }
-
- return display_form($q, \@errors, \%field_errors) if (@errors || scalar keys %field_errors); # Short circuit
-
- my $areas;
- if (defined $input{latitude} && defined $input{longitude}) {
- my $mapit_query = "4326/$input{longitude},$input{latitude}";
- $areas = mySociety::MaPit::call( 'point', $mapit_query );
- if ($input{council} =~ /^[\d,]+(\|[\d,]+)?$/) {
- my $no_details = $1 || '';
- my @area_types = Cobrand::area_types($cobrand);
- my %va = map { $_ => 1 } @area_types;
- my %councils;
- my $london = 0;
- foreach (keys %$areas) {
- $councils{$_} = 1 if $va{$areas->{$_}->{type}};
- $london = 1 if $areas->{$_}->{type} eq 'LBO' && $q->{site} ne 'emptyhomes';
- }
- my @input_councils = split /,|\|/, $input{council};
- foreach (@input_councils) {
- if (!$councils{$_}) {
- push(@errors, _('That location is not part of that council'));
- last;
- }
- }
-
- if ($no_details) {
- $input{council} =~ s/\Q$no_details\E//;
- @input_councils = split(/,/, $input{council});
- }
-
- # Check category here, won't be present if council is -1
- my @valid_councils = @input_councils;
- if ($london) {
- $field_errors{category} = _('Please choose a category')
- unless Utils::london_categories()->{$input{category}};
- @valid_councils = $input{council};
- } elsif ($input{category} && $q->{site} ne 'emptyhomes') {
- my $categories = select_all("select area_id from contacts
- where deleted='f' and area_id in ("
- . $input{council} . ') and category = ?', $input{category});
- $field_errors{category} = _('Please choose a category') unless @$categories;
- @valid_councils = map { $_->{area_id} } @$categories;
- foreach my $c (@valid_councils) {
- if ($no_details =~ /$c/) {
- push(@errors, _('We have details for that council'));
- $no_details =~ s/,?$c//;
- }
- }
- }
- $input{council} = join(',', @valid_councils) . $no_details;
- }
- $areas = ',' . join(',', sort keys %$areas) . ',';
- } elsif (defined $input{latitude} || defined $input{longitude}) {
- push(@errors, _('Somehow, you only have one co-ordinate. Please try again.'));
- } else {
- push(@errors, _("You haven't specified any sort of co-ordinates. Please try again."));
- }
-
- my $image;
- if ($fh) {
- try {
- $image = Page::process_photo($fh);
- } catch Error::Simple with {
- my $e = shift;
- $field_errors{photo} = sprintf(_("That image doesn't appear to have uploaded correctly (%s), please try again."), $e);
- };
- }
-
- if ($input{upload_fileid}) {
- open FP, mySociety::Config::get('UPLOAD_CACHE') . $input{upload_fileid};
- $image = join('', <FP>);
- close FP;
- }
-
- return display_form($q, \@errors, \%field_errors) if (@errors || scalar keys %field_errors);
-
- delete $input{council} if $input{council} eq '-1';
- my $used_map = $input{skipped} ? 'f' : 't';
- $input{category} = _('Other') unless $input{category};
- my ($id, $out);
- my $cobrand_data = Cobrand::extra_problem_data($cobrand, $q);
- if (my $token = $input{partial}) {
- my $id = mySociety::AuthToken::retrieve('partial', $token);
- if ($id) {
- dbh()->do("update problem set postcode=?, latitude=?, longitude=?, title=?, detail=?,
- name=?, email=?, phone=?, state='confirmed', council=?, used_map='t',
- anonymous=?, category=?, areas=?, cobrand=?, cobrand_data=?, confirmed=ms_current_timestamp(),
- lastupdate=ms_current_timestamp() where id=?", {}, $input{pc}, $input{latitude}, $input{longitude},
- $input{title}, $input{detail}, $input{name}, $input{email},
- $input{phone}, $input{council}, $input{anonymous} ? 'f' : 't',
- $input{category}, $areas, $cobrand, $cobrand_data, $id);
- Utils::workaround_pg_bytea('update problem set photo=? where id=?', 1, $image, $id)
- if $image;
- dbh()->commit();
- $out = $q->p(sprintf(_('You have successfully confirmed your report and you can now <a href="%s">view it on the site</a>.'), "/report/$id"));
- my $display_advert = Cobrand::allow_crosssell_adverts($cobrand);
- if ($display_advert) {
- $out .= CrossSell::display_advert($q, $input{email}, $input{name});
- }
- } else {
- $out = $q->p('There appears to have been a problem updating the details of your report.
-Please <a href="/contact">let us know what went on</a> and we\'ll look into it.');
- }
- } else {
- $id = dbh()->selectrow_array("select nextval('problem_id_seq');");
- Utils::workaround_pg_bytea("insert into problem
- (id, postcode, latitude, longitude, title, detail, name,
- email, phone, photo, state, council, used_map, anonymous, category, areas, lang, cobrand, cobrand_data)
- values
- (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'unconfirmed', ?, ?, ?, ?, ?, ?, ?, ?)", 10,
- $id, $input{pc}, $input{latitude}, $input{longitude}, $input{title},
- $input{detail}, $input{name}, $input{email}, $input{phone}, $image,
- $input{council}, $used_map, $input{anonymous} ? 'f': 't', $input{category},
- $areas, $mySociety::Locale::lang, $cobrand, $cobrand_data);
- my %h = ();
- $h{title} = $input{title};
- $h{detail} = $input{detail};
- $h{name} = $input{name};
- my $base = Page::base_url_with_lang($q, undef, 1);
- $h{url} = $base . '/P/' . mySociety::AuthToken::store('problem', $id);
- dbh()->commit();
-
- $out = Page::send_confirmation_email($q, $input{email}, $input{name}, 'problem', %h);
-
- }
- return $out;
-}
-
-sub display_form {
- my ($q, $errors, $field_errors) = @_;
- my @errors = @$errors;
- my %field_errors = %{$field_errors};
- my $cobrand = Page::get_cobrand($q);
- push @errors, _('There were problems with your report. Please see below.') if (scalar keys %field_errors);
-
- my ($pin_x, $pin_y, $pin_tile_x, $pin_tile_y) = (0,0,0,0);
- my @vars = qw(title detail name email phone pc latitude longitude x y skipped council anonymous partial upload_fileid);
-
- my %input = ();
- my %input_h = ();
-
- foreach my $key (@vars) {
- my $val = $q->param($key);
- $input{$key} = defined($val) ? $val : ''; # '0' is valid for longitude
- $input_h{$key} = ent( $input{$key} );
- }
-
- # Convert lat/lon to easting/northing if given
- # if ($input{lat}) {
- # try {
- # ($input{easting}, $input{northing}) = mySociety::GeoUtil::wgs84_to_national_grid($input{lat}, $input{lon}, 'G');
- # $input_h{easting} = $input{easting};
- # $input_h{northing} = $input{northing};
- # } catch Error::Simple with {
- # my $e = shift;
- # push @errors, "We had a problem with the supplied co-ordinates - outside the UK?";
- # };
- # }
-
- # Get tile co-ordinates if map clicked
- ($input{x}) = $input{x} =~ /^(\d+)/; $input{x} ||= 0;
- ($input{y}) = $input{y} =~ /^(\d+)/; $input{y} ||= 0;
- my @ps = $q->param;
- foreach (@ps) {
- ($pin_tile_x, $pin_tile_y, $pin_x) = ($1, $2, $q->param($_)) if /^tile_(\d+)\.(\d+)\.x$/;
- $pin_y = $q->param($_) if /\.y$/;
- }
-
- # We need either a map click, an E/N, to be skipping the map, or be filling in a partial form
- return display_location($q, @errors)
- unless ($pin_x && $pin_y)
- || ($input{latitude} && $input{longitude})
- || ($input{skipped} && $input{pc})
- || ($input{partial} && $input{pc});
-
- # Work out some co-ordinates from whatever we've got
- my ($latitude, $longitude);
- if ($input{skipped}) {
- # Map is being skipped
- if ( length $input{latitude} && length $input{longitude} ) {
- $latitude = $input{latitude};
- $longitude = $input{longitude};
- } else {
- my ( $lat, $lon, $error ) =
- FixMyStreet::Geocode::lookup( $input{pc}, $q );
- $latitude = $lat;
- $longitude = $lon;
- }
- } elsif ($pin_x && $pin_y) {
-
- # Map was clicked on (tilma, or non-JS OpenLayers, for example)
- ($latitude, $longitude) = FixMyStreet::Map::click_to_wgs84($q, $pin_tile_x, $pin_x, $pin_tile_y, $pin_y);
-
- } elsif ( $input{partial} && $input{pc} && !length $input{latitude} && !length $input{longitude} ) {
- my $error;
- try {
- ($latitude, $longitude, $error) = FixMyStreet::Geocode::lookup($input{pc}, $q);
- } catch Error::Simple with {
- $error = shift;
- };
- return FixMyStreet::Geocode::list_choices($error, '/', $q) if ref($error) eq 'ARRAY';
- return front_page($q, $error) if $error;
- } else {
- # Normal form submission
- $latitude = $input_h{latitude};
- $longitude = $input_h{longitude};
- }
-
- # Shrink, as don't need accuracy plus we want them as English strings
- ($latitude, $longitude) = map { Utils::truncate_coordinate($_) } ( $latitude, $longitude );
-
- # Look up councils and do checks for the point we've got
- my @area_types = Cobrand::area_types($cobrand);
- # XXX: I think we want in_gb_locale around the next line, needs testing
- my $all_councils = mySociety::MaPit::call('point', "4326/$longitude,$latitude", type => \@area_types);
-
- # Let cobrand do a check
- my ($success, $error_msg) = Cobrand::council_check($cobrand, { all_councils => $all_councils }, $q, 'submit_problem');
- if (!$success) {
- return front_page($q, $error_msg);
- }
-
- if (mySociety::Config::get('COUNTRY') eq 'GB') {
- # Ipswich & St Edmundsbury are responsible for everything in their areas, not Suffolk
- delete $all_councils->{2241} if $all_councils->{2446} || $all_councils->{2443};
-
- # Norwich is responsible for everything in its areas, not Norfolk
- delete $all_councils->{2233} if $all_councils->{2391};
-
- } elsif (mySociety::Config::get('COUNTRY') eq 'NO') {
-
- # Oslo is both a kommune and a fylke, we only want to show it once
- delete $all_councils->{301} if $all_councils->{3};
-
- }
-
- return display_location($q, _('That spot does not appear to be covered by a council.
-If you have tried to report an issue past the shoreline, for example,
-please specify the closest point on land.')) unless %$all_councils;
-
- # Look up categories for this council or councils
- my $category = '';
- my (%council_ok, @categories);
- my $categories = select_all("select area_id, category from contacts
- where deleted='f' and area_id in (" . join(',', keys %$all_councils) . ')');
- my $first_council = (values %$all_councils)[0];
- if ($q->{site} eq 'emptyhomes') {
- foreach (@$categories) {
- $council_ok{$_->{area_id}} = 1;
- }
- @categories = (_('-- Pick a property type --'), _('Empty house or bungalow'),
- _('Empty flat or maisonette'), _('Whole block of empty flats'),
- _('Empty office or other commercial'), _('Empty pub or bar'),
- _('Empty public building - school, hospital, etc.'));
- $category = _('Property type:');
- } elsif ($first_council->{type} eq 'LBO') {
- $council_ok{$first_council->{id}} = 1;
- @categories = (_('-- Pick a category --'), sort keys %{ Utils::london_categories() } );
- $category = _('Category:');
- } else {
- @$categories = sort { strcoll($a->{category}, $b->{category}) } @$categories;
- my %seen;
- foreach (@$categories) {
- $council_ok{$_->{area_id}} = 1;
- next if $_->{category} eq _('Other');
- next if $q->{site} eq 'southampton' && $_->{category} eq 'Street lighting';
- push @categories, $_->{category} unless $seen{$_->{category}};
- $seen{$_->{category}} = 1;
- }
- if ($q->{site} eq 'scambs') {
- @categories = Page::scambs_categories();
- }
- if (@categories) {
- @categories = (_('-- Pick a category --'), @categories, _('Other'));
- $category = _('Category:');
- }
- }
- $category = $q->label({'for'=>'form_category'}, $category) .
- $q->popup_menu(-name=>'category', -values=>\@categories, -id=>'form_category',
- -attributes=>{id=>'form_category'})
- if $category;
-
- # Work out what help text to show, depending on whether we have council details
- my @councils = keys %council_ok;
- my $details;
- if (@councils == scalar keys %$all_councils) {
- $details = 'all';
- } elsif (@councils == 0) {
- $details = 'none';
- } else {
- $details = 'some';
- }
-
- # Forms that allow photos need a different enctype
- my $allow_photo_upload = Cobrand::allow_photo_upload($cobrand);
- my $enctype = '';
- if ($allow_photo_upload) {
- $enctype = ' enctype="multipart/form-data"';
- }
-
- my %vars;
- $vars{input_h} = \%input_h;
- $vars{field_errors} = \%field_errors;
- if ($input{skipped}) {
- my $cobrand_form_elements = Cobrand::form_elements($cobrand, 'mapSkippedForm', $q);
- my $form_action = Cobrand::url($cobrand, '/', $q);
- $vars{form_start} = <<EOF;
-<form action="$form_action" method="post" name="mapSkippedForm"$enctype>
-<input type="hidden" name="latitude" value="$latitude">
-<input type="hidden" name="longitude" value="$longitude">
-<input type="hidden" name="pc" value="$input_h{pc}">
-<input type="hidden" name="skipped" value="1">
-$cobrand_form_elements
-<div id="skipped-map">
-EOF
- } else {
- my $type;
- if ($allow_photo_upload) {
- $type = 2;
- } else {
- $type = 1;
- }
- $vars{form_start} = FixMyStreet::Map::display_map($q,
- latitude => $latitude, longitude => $longitude,
- type => $type,
- pins => [ [ $latitude, $longitude, 'purple' ] ],
- );
- my $partial_id;
- if (my $token = $input{partial}) {
- $partial_id = mySociety::AuthToken::retrieve('partial', $token);
- if ($partial_id) {
- $vars{form_start} .=
- $q->p({ id => 'unknown' },
- _('Please note your report has <strong>not yet been sent</strong>. Choose a category and add further information below, then submit.'));
- }
- }
- $vars{text_located} = $q->p(_('You have located the problem at the point marked with a purple pin on the map.
-If this is not the correct location, simply click on the map again. '));
- }
- $vars{page_heading} = $q->h1(_('Reporting a problem'));
-
- if ($details eq 'all') {
- my $council_list = join('</strong>' . _(' or ') . '<strong>', map { $_->{name} } values %$all_councils);
- if ($q->{site} eq 'emptyhomes') {
- $vars{text_help} = '<p>' . sprintf(_('All the information you provide here will be sent to <strong>%s</strong>.
-On the site, we will show the subject and details of the problem, plus your
-name if you give us permission.'), $council_list);
- } elsif ($first_council->{type} eq 'LBO') {
- $vars{text_help} = '<p>' . sprintf(_('All the information you
- provide here will be sent to <strong>%s</strong> or a relevant
- local body such as TfL, via the London Report-It system. The
- subject and details of the problem will be public, plus your name
- if you give us permission.'), $council_list);
- } else {
- $vars{text_help} = '<p>' . sprintf(_('All the information you provide here will be sent to <strong>%s</strong>.
-The subject and details of the problem will be public, plus your
-name if you give us permission.'), $council_list);
- }
- $vars{text_help} .= '<input type="hidden" name="council" value="' . join(',', keys %$all_councils) . '">';
- } elsif ($details eq 'some') {
- my $e = Cobrand::contact_email($cobrand);
- my %councils = map { $_ => 1 } @councils;
- my @missing;
- foreach (keys %$all_councils) {
- push @missing, $_ unless $councils{$_};
- }
- my $n = @missing;
- my $list = join(_(' or '), map { $all_councils->{$_}->{name} } @missing);
- $vars{text_help} = '<p>' . _('All the information you provide here will be sent to') . ' <strong>'
- . join('</strong>' . _(' or ') . '<strong>', map { $all_councils->{$_}->{name} } @councils)
- . '</strong>. ';
- $vars{text_help} .= _('The subject and details of the problem will be public, plus your name if you give us permission.');
- $vars{text_help} .= ' ' . mySociety::Locale::nget(
- 'We do <strong>not</strong> yet have details for the other council that covers this location.',
- 'We do <strong>not</strong> yet have details for the other councils that cover this location.',
- $n
- );
- $vars{text_help} .= ' ' . sprintf(_("You can help us by finding a contact email address for local problems for %s and emailing it to us at <a href='mailto:%s'>%s</a>."), $list, $e, $e);
- $vars{text_help} .= '<input type="hidden" name="council" value="' . join(',', @councils)
- . '|' . join(',', @missing) . '">';
- } else {
- my $e = Cobrand::contact_email($cobrand);
- my $list = join(_(' or '), map { $_->{name} } values %$all_councils);
- my $n = scalar keys %$all_councils;
- if ($q->{site} ne 'emptyhomes') {
- $vars{text_help} = '<p>';
- $vars{text_help} .= mySociety::Locale::nget(
- 'We do not yet have details for the council that covers this location.',
- 'We do not yet have details for the councils that cover this location.',
- $n
- );
- $vars{text_help} .= _("If you submit a problem here the subject and details of the problem will be public, but the problem will <strong>not</strong> be reported to the council.");
- $vars{text_help} .= sprintf(_("You can help us by finding a contact email address for local problems for %s and emailing it to us at <a href='mailto:%s'>%s</a>."), $list, $e, $e);
- } else {
- $vars{text_help} = '<p>'
- . _('We do not yet have details for the council that covers this location.')
- . ' '
- . _("If you submit a report here it will be left on the site, but not reported to the council &ndash; please still leave your report, so that we can show to the council the activity in their area.");
- }
- $vars{text_help} .= '<input type="hidden" name="council" value="-1">';
- }
-
- if ($input{skipped}) {
- $vars{text_help} .= $q->p(_('Please fill in the form below with details of the problem,
-and describe the location as precisely as possible in the details box.'));
- } elsif ($q->{site} eq 'scambs') {
- $vars{text_help} .= '<p>Please fill in details of the problem below. We won\'t be able
-to help unless you leave as much detail as you can, so please describe the exact location of
-the problem (e.g. on a wall), what it is, how long it has been there, a description (and a
-photo of the problem if you have one), etc.';
- } elsif ($q->{site} eq 'emptyhomes') {
- $vars{text_help} .= $q->p(_(<<EOF));
-Please fill in details of the empty property below, saying what type of
-property it is e.g. an empty home, block of flats, office etc. Tell us
-something about its condition and any other information you feel is relevant.
-There is no need for you to give the exact address. Please be polite, concise
-and to the point; writing your message entirely in block capitals makes it hard
-to read, as does a lack of punctuation.
-EOF
- } elsif ($details ne 'none') {
- $vars{text_help} .= $q->p(_('Please fill in details of the problem below. The council won\'t be able
-to help unless you leave as much detail as you can, so please describe the exact location of
-the problem (e.g. on a wall), what it is, how long it has been there, a description (and a
-photo of the problem if you have one), etc.'));
- } else {
- $vars{text_help} .= $q->p(_('Please fill in details of the problem below.'));
- }
-
- if (@errors) {
- $vars{errors} = '<ul class="error"><li>' . join('</li><li>', @errors) . '</li></ul>';
- }
-
- $vars{anon} = ($input{anonymous}) ? ' checked' : ($input{title} ? '' : ' checked');
-
- $vars{form_heading} = $q->h2(_('Empty property details form')) if $q->{site} eq 'emptyhomes';
- $vars{subject_label} = _('Subject:');
- $vars{detail_label} = _('Details:');
- $vars{photo_label} = _('Photo:');
- $vars{name_label} = _('Name:');
- $vars{email_label} = _('Email:');
- $vars{phone_label} = _('Phone:');
- $vars{optional} = _('(optional)');
- if ($q->{site} eq 'emptyhomes') {
- $vars{anonymous} = _('Can we show your name on the site?');
- } else {
- $vars{anonymous} = _('Can we show your name publicly?');
- }
- $vars{anonymous2} = _('(we never show your email address or phone number)');
-
- my $partial_id;
- if (my $token = $input{partial}) {
- $partial_id = mySociety::AuthToken::retrieve('partial', $token);
- if ($partial_id) {
- $vars{partial_field} = '<input type="hidden" name="partial" value="' . $token . '">';
- $vars{partial_field} .= '<input type="hidden" name="has_photo" value="' . $q->param('has_photo') . '">';
- }
- }
- my $photo_input = '';
- if ($allow_photo_upload) {
- $photo_input = <<EOF;
-<div id="fileupload_normalUI">
-<label for="form_photo">$vars{photo_label}</label>
-<input type="file" name="photo" id="form_photo">
-</div>
-EOF
- }
- if ($partial_id && $q->param('has_photo')) {
- $vars{photo_field} = "<p>The photo you uploaded was:</p> <p><img src='/photo?id=$partial_id'></p>";
- } else {
- $vars{photo_field} = $photo_input;
- }
-
- if ($q->{site} ne 'emptyhomes') {
- $vars{text_notes} =
- $q->p(_("Please note:")) .
- "<ul>" .
- $q->li(_("We will only use your personal information in accordance with our <a href=\"/faq#privacy\">privacy policy.</a>")) .
- $q->li(_("Please be polite, concise and to the point.")) .
- $q->li(_("Please do not be abusive &mdash; abusing your council devalues the service for all users.")) .
- $q->li(_("Writing your message entirely in block capitals makes it hard to read, as does a lack of punctuation.")) .
- $q->li(_("Remember that FixMyStreet is primarily for reporting physical problems that can be fixed. If your problem is not appropriate for submission via this site remember that you can contact your council directly using their own website."));
- $vars{text_notes} .=
- $q->li(_("FixMyStreet and the Guardian are providing this service in partnership in <a href=\"/faq#privacy\">certain cities</a>. In those cities, both have access to any information submitted, including names and email addresses, and will use it only to ensure the smooth running of the service, in accordance with their privacy policies."))
- if mySociety::Config::get('COUNTRY') eq 'GB';
- $vars{text_notes} .= "</ul>\n";
- }
-
- %vars = (%vars,
- category => $category,
- map_end => FixMyStreet::Map::display_map_end(1),
- url_home => Cobrand::url($cobrand, '/', $q),
- submit_button => _('Submit')
- );
- return (Page::template_include('report-form', $q, Page::template_root($q), %vars),
- robots => 'noindex,nofollow',
- js => FixMyStreet::Map::header_js(),
- );
-}
-
-# redirect from osgb
-sub redirect_from_osgb_to_wgs84 {
- my ($q) = @_;
-
- my $e = $q->param('e');
- my $n = $q->param('n');
-
- my ( $lat, $lon ) = Utils::convert_en_to_latlon_truncated( $e, $n );
-
- my $lat_lon_url = NewURL(
- $q,
- -retain => 1,
- e => undef,
- n => undef,
- lat => $lat,
- lon => $lon
- );
-
- print $q->redirect(
- -location => $lat_lon_url,
- -status => 301, # permanent
- );
-
- return '';
-}
-
-sub display_location {
- my ($q, @errors) = @_;
- my $cobrand = Page::get_cobrand($q);
- my @vars = qw(pc x y lat lon all_pins no_pins);
-
- my %input = ();
- my %input_h = ();
-
- foreach my $key (@vars) {
- my $val = $q->param($key);
- $input{$key} = defined($val) ? $val : ''; # '0' is valid for longitude
- $input_h{$key} = ent( $input{$key} );
- }
-
- my $latitude = $input{lat};
- my $longitude = $input{lon};
-
- # X/Y referring to tiles old-school
- (my $x) = $input{x} =~ /^(\d+)/; $x ||= 0;
- (my $y) = $input{y} =~ /^(\d+)/; $y ||= 0;
-
- return front_page( $q, @errors )
- unless ( $x && $y )
- || $input{pc}
- || ( $latitude ne '' && $longitude ne '' );
-
- if ( $x && $y ) {
-
- # Convert the tile co-ordinates to real ones.
- ( $latitude, $longitude ) =
- FixMyStreet::Map::tile_xy_to_wgs84( $x, $y );
- }
- elsif ( $latitude && $longitude ) {
-
- # Don't need to do anything
- }
- else {
- my $error;
- try {
- ( $latitude, $longitude, $error ) =
- FixMyStreet::Geocode::lookup( $input{pc}, $q );
-
- debug 'Looked up postcode "%s": lat: "%s", lon: "%s", error: "%s"',
- $input{pc}, $latitude, $longitude, $error;
- }
- catch Error::Simple with {
- $error = shift;
- };
- return FixMyStreet::Geocode::list_choices( $error, '/', $q )
- if ( ref($error) eq 'ARRAY' );
- return front_page( $q, $error ) if $error;
- }
-
- # Check this location is okay to be displayed for the cobrand
- my ($success, $error_msg) = Cobrand::council_check($cobrand, { lat => $latitude, lon => $longitude }, $q, 'display_location');
- return front_page($q, $error_msg) unless $success;
-
- # Deal with pin hiding/age
- my ($hide_link, $hide_text, $all_link, $all_text, $interval);
- if ($input{all_pins}) {
- $all_link = NewURL($q, -retain=>1, no_pins=>undef, all_pins=>undef);
- $all_text = _('Hide stale reports');
- } else {
- $all_link = NewURL($q, -retain=>1, no_pins=>undef, all_pins=>1);
- $all_text = _('Include stale reports');
- $interval = '6 months';
- }
-
- my ($on_map_all, $on_map, $around_map, $dist) = FixMyStreet::Map::map_features($q, $latitude, $longitude, $interval);
- my @pins;
- foreach (@$on_map_all) {
- push @pins, [ $_->{latitude}, $_->{longitude}, ($_->{state} eq 'fixed' ? 'green' : 'red'), $_->{id} ];
- }
- my $on_list = '';
- foreach (@$on_map) {
- my $report_url = NewURL($q, -url => '/report/' . $_->{id});
- $report_url = Cobrand::url($cobrand, $report_url, $q);
- $on_list .= '<li><a href="' . $report_url . '">';
- $on_list .= ent($_->{title}) . '</a> <small>(';
- $on_list .= Page::prettify_epoch($q, $_->{time}, 1) . ')</small>';
- $on_list .= ' <small>' . _('(fixed)') . '</small>' if $_->{state} eq 'fixed';
- $on_list .= '</li>';
- }
- $on_list = $q->li(_('No problems have been reported yet.'))
- unless $on_list;
-
- my $around_list = '';
- foreach (@$around_map) {
- my $report_url = Cobrand::url($cobrand, NewURL($q, -url => '/report/' . $_->{id}), $q);
- $around_list .= '<li><a href="' . $report_url . '">';
- my $dist = int($_->{distance}*10+0.5);
- $dist = $dist / 10;
- $around_list .= ent($_->{title}) . '</a> <small>(';
- $around_list .= Page::prettify_epoch($q, $_->{time}, 1) . ', ';
- $around_list .= $dist . 'km)</small>';
- $around_list .= ' <small>' . _('(fixed)') . '</small>' if $_->{state} eq 'fixed';
- $around_list .= '</li>';
- push @pins, [ $_->{latitude}, $_->{longitude}, ($_->{state} eq 'fixed' ? 'green' : 'red'), $_->{id} ];
- }
- $around_list = $q->li(_('No problems found.'))
- unless $around_list;
-
- if ($input{no_pins}) {
- $hide_link = NewURL($q, -retain=>1, no_pins=>undef);
- $hide_text = _('Show pins');
- @pins = ();
- } else {
- $hide_link = NewURL($q, -retain=>1, no_pins=>1);
- $hide_text = _('Hide pins');
- }
- my $map_links = "<p id='sub_map_links'><a id='hide_pins_link' rel='nofollow' href='$hide_link'>$hide_text</a>";
- if (mySociety::Config::get('COUNTRY') eq 'GB') {
- $map_links .= " | <a id='all_pins_link' rel='nofollow' href='$all_link'>$all_text</a></p> <input type='hidden' id='all_pins' name='all_pins' value='$input_h{all_pins}'>";
- } else {
- $map_links .= "</p>";
- }
-
- # truncate the lat,lon for nicer rss urls, and strings for outputting
- my ( $short_lat, $short_lon ) =
- map { Utils::truncate_coordinate($_) } #
- ( $latitude, $longitude );
-
- my $url_skip = NewURL($q, -retain=>1,
- x => undef, 'y' => undef,
- latitude => $short_lat, longitude => $short_lon,
- 'submit_map'=>1, skipped=>1
- );
- my $pc_h = ent($q->param('pc') || '');
-
- my $rss_url;
- if ($pc_h) {
- $rss_url = "/rss/pc/" . URI::Escape::uri_escape_utf8($pc_h);
- } else {
- $rss_url = "/rss/l/$short_lat,$short_lon";
- }
- $rss_url = Cobrand::url( $cobrand, NewURL($q, -url=> $rss_url), $q);
-
- my %vars = (
- 'map' => FixMyStreet::Map::display_map($q,
- latitude => $short_lat, longitude => $short_lon,
- type => 1,
- pins => \@pins,
- post => $map_links
- ),
- map_end => FixMyStreet::Map::display_map_end(1),
- url_home => Cobrand::url($cobrand, '/', $q),
- url_rss => $rss_url,
- url_email => Cobrand::url($cobrand, NewURL($q, lat => $short_lat, lon => $short_lon, -url=>'/alert', feed=>"local:$short_lat:$short_lon"), $q),
- url_skip => $url_skip,
- email_me => _('Email me new local problems'),
- rss_alt => _('RSS feed'),
- rss_title => _('RSS feed of recent local problems'),
- reports_on_around => $on_list,
- reports_nearby => $around_list,
- heading_problems => _('Problems in this area'),
- heading_on_around => _('Reports on and around the map'),
- heading_closest => sprintf(_('Closest nearby problems <small>(within&nbsp;%skm)</small>'), $dist),
- distance => $dist,
- pc_h => $pc_h,
- errors => @errors ? '<ul class="error"><li>' . join('</li><li>', @errors) . '</li></ul>' : '',
- text_to_report => _('To report a problem, simply
- <strong>click on the map</strong> at the correct location.'),
- text_skip => sprintf(_("<small>If you cannot see the map, <a href='%s' rel='nofollow'>skip this
- step</a>.</small>"), $url_skip),
- );
-
- my %params = (
- rss => [ _('Recent local problems, FixMyStreet'), $rss_url ],
- js => FixMyStreet::Map::header_js(),
- robots => 'noindex,nofollow',
- );
-
- return (Page::template_include('map', $q, Page::template_root($q), %vars), %params);
-}
-
-sub display_problem {
- my ($q, $errors, $field_errors) = @_;
- my @errors = @$errors;
- my %field_errors = %{$field_errors};
- my $cobrand = Page::get_cobrand($q);
- push @errors, _('There were problems with your update. Please see below.') if (scalar keys %field_errors);
-
- my @vars = qw(id name rznvy update fixed add_alert upload_fileid submit_update);
- my %input = map { $_ => $q->param($_) || '' } @vars;
- my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
- my $base = Cobrand::base_url($cobrand);
-
- # Some council with bad email software
- if ($input{id} =~ /^3D\d+$/) {
- $input{id} =~ s/^3D//;
- print $q->redirect(-location => $base . '/report/' . $input{id}, -status => 301);
- return '';
- }
-
- # Redirect old /?id=NNN URLs to /report/NNN
- if (!@errors && !scalar keys %field_errors && $ENV{SCRIPT_URL} eq '/') {
- print $q->redirect(-location => $base . '/report/' . $input{id}, -status => 301);
- return '';
- }
-
- # Get all information from database
- return display_location($q, _('Unknown problem ID')) if !$input{id} || $input{id} =~ /\D/;
- my $problem = Problems::fetch_problem($input{id});
- return display_location($q, _('Unknown problem ID')) unless $problem;
- return front_page($q, _('That report has been removed from FixMyStreet.'), '410 Gone') if $problem->{state} eq 'hidden';
-
- my $extra_data = Cobrand::extra_data($cobrand, $q);
- my $google_link = Cobrand::base_url_for_emails($cobrand, $extra_data)
- . '/report/' . $problem->{id};
-
- # truncate the lat,lon for nicer rss urls
- my ( $short_lat, $short_lon ) =
- map { Utils::truncate_coordinate($_) } #
- ( $problem->{latitude}, $problem->{longitude} );
-
- my $map_links = '';
- $map_links = "<p id='sub_map_links'>"
- . "<a href=\"http://maps.google.co.uk/maps?output=embed&amp;z=16&amp;q="
- . URI::Escape::uri_escape_utf8( $problem->{title} . ' - ' . $google_link )
- . "\@$short_lat,$short_lon\">View on Google Maps</a></p>"
- if mySociety::Config::get('COUNTRY') eq 'GB';
-
- my $banner;
- if ($q->{site} ne 'emptyhomes' && $problem->{state} eq 'confirmed' && $problem->{duration} > 8*7*24*60*60) {
- $banner = $q->p({id => 'unknown'}, _('This problem is old and of unknown status.'));
- }
- if ($problem->{state} eq 'fixed') {
- $banner = $q->p({id => 'fixed'}, _('This problem has been fixed') . '.');
- }
-
- my $contact_url = Cobrand::url($cobrand, NewURL($q, -retain => 1, pc => undef, x => undef, 'y' => undef, -url=>'/contact?id=' . $input{id}), $q);
- my $back = Cobrand::url($cobrand, NewURL($q, -url => '/',
- lat => $short_lat, lon => $short_lon,
- -retain => 1, pc => undef, x => undef, 'y' => undef, id => undef
- ), $q);
- my $fixed = ($input{fixed}) ? ' checked' : '';
-
- my %vars = (
- banner => $banner,
- map_start => FixMyStreet::Map::display_map($q,
- latitude => $problem->{latitude}, longitude => $problem->{longitude},
- type => 0,
- pins => $problem->{used_map} ? [ [ $problem->{latitude}, $problem->{longitude}, 'blue' ] ] : [],
- post => $map_links
- ),
- map_end => FixMyStreet::Map::display_map_end(0),
- problem_title => ent($problem->{title}),
- problem_meta => Page::display_problem_meta_line($q, $problem),
- problem_detail => Page::display_problem_detail($problem),
- problem_photo => Page::display_problem_photo($q, $problem),
- problem_updates => Page::display_problem_updates($input{id}, $q),
- unsuitable => $q->a({rel => 'nofollow', href => $contact_url}, _('Offensive? Unsuitable? Tell us')),
- more_problems => '<a href="' . $back . '">' . _('More problems nearby') . '</a>',
- url_home => Cobrand::url($cobrand, '/', $q),
- alert_link => Cobrand::url($cobrand, NewURL($q, -url => '/alert?type=updates;id='.$input_h{id}, -retain => 1, pc => undef, x => undef, 'y' => undef ), $q),
- alert_text => _('Email me updates'),
- email_label => _('Email:'),
- subscribe => _('Subscribe'),
- blurb => _('Receive email when updates are left on this problem'),
- cobrand_form_elements1 => Cobrand::form_elements($cobrand, 'alerts', $q),
- form_alert_action => Cobrand::url($cobrand, '/alert', $q),
- rss_url => Cobrand::url($cobrand, NewURL($q, -retain=>1, -url => '/rss/'.$input_h{id}, pc => undef, x => undef, 'y' => undef, id => undef), $q),
- rss_title => _('RSS feed'),
- rss_alt => _('RSS feed of updates to this problem'),
- update_heading => $q->h2(_('Provide an update')),
- field_errors => \%field_errors,
- add_alert_checked => ($input{add_alert} || !$input{submit_update}) ? ' checked' : '',
- fixedline_box => $problem->{state} eq 'fixed' ? '' : qq{<input type="checkbox" name="fixed" id="form_fixed" value="1"$fixed>},
- fixedline_label => $problem->{state} eq 'fixed' ? '' : qq{<label for="form_fixed">} . _('This problem has been fixed') . qq{</label>},
- name_label => _('Name:'),
- update_label => _('Update:'),
- alert_label => _('Alert me to future updates'),
- post_label => _('Post'),
- cobrand_form_elements => Cobrand::form_elements($cobrand, 'updateForm', $q),
- form_action => Cobrand::url($cobrand, '/', $q),
- input_h => \%input_h,
- optional => _('(optional)'),
- );
-
- $vars{update_blurb} = $q->p($q->small(_('Please note that updates are not sent to the council. If you leave your name it will be public. Your information will only be used in accordance with our <a href="/faq#privacy">privacy policy</a>')))
- unless $q->{site} eq 'emptyhomes'; # No council blurb
-
- if (@errors) {
- $vars{errors} = '<ul class="error"><li>' . join('</li><li>', @errors) . '</li></ul>';
- }
-
- my $allow_photo_upload = Cobrand::allow_photo_upload($cobrand);
- if ($allow_photo_upload) {
- my $photo_label = _('Photo:');
- $vars{enctype} = ' enctype="multipart/form-data"';
- $vars{photo_element} = <<EOF;
-<div id="fileupload_normalUI">
-<label for="form_photo">$photo_label</label>
-<input type="file" name="photo" id="form_photo">
-</div>
-EOF
- }
-
- my %params = (
- rss => [ _('Updates to this problem, FixMyStreet'), "/rss/$input_h{id}" ],
- robots => 'index, nofollow',
- js => FixMyStreet::Map::header_js(),
- title => $problem->{title}
- );
-
- my $page = Page::template_include('problem', $q, Page::template_root($q), %vars);
- return ($page, %params);
-}
-
diff --git a/web/iphone/index.cgi b/web/iphone/index.cgi
deleted file mode 100755
index cd5199482..000000000
--- a/web/iphone/index.cgi
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/perl -w -I../../perllib -I../../commonlib/perllib
-
-# iphone/index.cgi:
-# Screenshots of the iPhone FixMyStreet application, showing the flow
-#
-# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: index.cgi,v 1.1 2008-10-29 15:30:15 matthew Exp $
-
-use strict;
-use Standard -db;
-use mySociety::Config;
-use mySociety::Web qw(ent);
-
-# XXX: Ugh, as we're in a subdirectory
-BEGIN {
- mySociety::Config::set_file("$FindBin::Bin/../../conf/general");
-}
-
-my @screens = (
-"iphone-1start.png", 'Click the image to progress through the flow of using the iPhone FixMyStreet application.
-<br>When launched, the user&rsquo;s location automatically gets fetched&hellip;',
-"iphone-2locfound.png", 'They want to take a photo.',
-"iphone-pickpicture1.png", 'The simulator doesn&rsquo;t have a camera, so we&rsquo;re taken to the photo albums. Let&rsquo;s pick Hawaii.',
-"iphone-pickpicture2.png", 'That red clouds photo looks nice.',
-"iphone-pickpicture3.png", 'After any moving or scaling we want, we select the photo.',
-"iphone-3picture.png", 'Okay, now we need to edit the summary of the report.',
-"iphone-editsummary.png", 'Enter some text.',
-"iphone-editsummary2.png", 'And done.',
-"iphone-4subject.png", 'I haven&rsquo;t entered all my details yet, so that&rsquo;s next.',
-"iphone-5details.png", 'Your details are remembered so you only have to enter them once.',
-"iphone-6emailkeyboard.png", 'The iPhone has different keyboards, this is the email one.',
-"iphone-5details.png", 'Right, we need to enter a name.',
-"iphone-editname.png", 'Slightly different keyboard to the email one.',
-"iphone-detailsdone.png", 'Okay, details entered.',
-"iphone-allready.png", 'That&rsquo;s everything, hit Report!',
-"iphone-7uploading.png", 'Uploading&hellip;',
-"iphone-8response.png", 'The simulator always thinks it&rsquo;s in the US, which FixMyStreet won&rsquo;t like very much.',
-"iphone-allready.png", 'Ah well, let&rsquo;s read the About page instead',
-"iphone-9about.png", 'Donate? :)',
-);
-
-sub main {
- my $q = shift;
- print Page::header($q, title=>'FixMyStreet for iPhone screenshots');
- print '<h1>iPhone simulator simulator</h1>';
- my $screens = scalar @screens / 2;
- print <<EOF;
-<script type="text/javascript">
-document.write('<style type="text/css">.vv { display: none; }</style>');
-function show(a) {
- if (a==$screens) b = 1;
- else b = a+1;
- document.getElementById('d' + a).style.display='none';
- document.getElementById('d' + b).style.display='block';
-}
-</script>
-EOF
- for (my $i=0; $i<@screens; $i+=2) {
- my $t = $i/2 + 1;
- my $next = $t + 1;
- print "<div id='d$t'";
- print " class='vv'" if $i>1;
- print ">";
- print "<p>$screens[$i+1]</p>";
- print "<p align='center'><a onclick='show($t);return false' href='#d$next'><img src='$screens[$i]' width=414 border=0 height=770></a></p>";
- print '</div>';
- }
- print Page::footer($q);
-}
-Page::do_fastcgi(\&main);
-
diff --git a/web/js.js b/web/js.js
deleted file mode 100644
index 07bede2d7..000000000
--- a/web/js.js
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * js.js
- * FixMyStreet JavaScript
- */
-
-
-YAHOO.util.Event.onContentReady('pc', function() {
- if (this.id && this.value == this.defaultValue) {
- this.focus();
- }
-});
-
-YAHOO.util.Event.onContentReady('mapForm', function() {
- this.onsubmit = function() {
- if (this.submit_problem) {
- this.onsubmit = function() { return false; };
- }
-
- /* XXX Should be in Tilma code only */
- if (this.x) {
- this.x.value = fixmystreet.x + 3;
- this.y.value = fixmystreet.y + 3;
- }
-
- /*
- if (swfu && swfu.getStats().files_queued > 0) {
- swfu.startUpload();
- return false;
- }
- */
- return true;
- }
-});
-
-YAHOO.util.Event.onContentReady('another_qn', function() {
- if (!document.getElementById('been_fixed_no').checked && !document.getElementById('been_fixed_unknown').checked) {
- YAHOO.util.Dom.setStyle(this, 'display', 'none');
- }
- YAHOO.util.Event.addListener('been_fixed_no', 'click', function(e) {
- YAHOO.util.Dom.setStyle('another_qn', 'display', 'block');
- });
- YAHOO.util.Event.addListener('been_fixed_unknown', 'click', function(e) {
- YAHOO.util.Dom.setStyle('another_qn', 'display', 'block');
- });
- YAHOO.util.Event.addListener('been_fixed_yes', 'click', function(e) {
- YAHOO.util.Dom.setStyle('another_qn', 'display', 'none');
- });
-});
-
-var timer;
-function email_alert_close() {
- YAHOO.util.Dom.setStyle('email_alert_box', 'display', 'none');
-}
-YAHOO.util.Event.onContentReady('email_alert', function() {
- YAHOO.util.Event.addListener(this, 'click', function(e) {
- if (!document.getElementById('email_alert_box'))
- return true;
- YAHOO.util.Event.preventDefault(e);
- if (YAHOO.util.Dom.getStyle('email_alert_box', 'display') == 'block') {
- email_alert_close();
- } else {
- var pos = YAHOO.util.Dom.getXY(this);
- pos[0] -= 20; pos[1] += 20;
- YAHOO.util.Dom.setStyle('email_alert_box', 'display', 'block');
- YAHOO.util.Dom.setXY('email_alert_box', pos);
- document.getElementById('alert_rznvy').focus();
- }
- });
- YAHOO.util.Event.addListener(this, 'mouseout', function(e) {
- timer = window.setTimeout(email_alert_close, 2000);
- });
- YAHOO.util.Event.addListener(this, 'mouseover', function(e) {
- window.clearTimeout(timer);
- });
-});
-YAHOO.util.Event.onContentReady('email_alert_box', function() {
- YAHOO.util.Event.addListener(this, 'mouseout', function(e) {
- timer = window.setTimeout(email_alert_close, 2000);
- });
- YAHOO.util.Event.addListener(this, 'mouseover', function(e) {
- window.clearTimeout(timer);
- });
-});
-
-/* File upload */
-/*
-function doSubmit(e) {
- e = e || window.event;
- if (e.stopPropagation) e.stopPropagation();
- e.cancelBubble = true;
- try {
- if (swfu.getStats().files_queued > 0)
- swfu.startUpload();
- else
- return true;
- } catch (e) {}
- return false;
-}
-
-function uploadDone() {
- var m = document.getElementById('mapForm');
- if (m) {
- m.submit();
- } else {
- document.getElementById('fieldset').submit();
- }
-}
-
-var swfu;
-var swfu_settings = {
- upload_url : "http://matthew.bci.mysociety.org/upload.cgi",
- flash_url : "http://matthew.bci.mysociety.org/jslib/swfupload/swfupload_f9.swf",
- file_size_limit : "10240",
- file_types : "*.jpg;*.jpeg;*.pjpeg",
- file_types_description : "JPEG files",
- file_upload_limit : "0",
-
- swfupload_loaded_handler : function() {
- var d = document.getElementById("fieldset");
- if (d) d.onsubmit = doSubmit;
- },
- file_queued_handler : function(obj) {
- document.getElementById('txtfilename').value = obj.name;
- },
- file_queue_error_handler : fileQueueError,
-//upload_start_handler : uploadStartEventHandler,
- upload_progress_handler : function(obj, bytesLoaded, bytesTotal) {
- var percent = Math.ceil((bytesLoaded / bytesTotal) * 100);
- obj.id = "singlefile";
- var progress = new FileProgress(obj, this.customSettings.progress_target);
- progress.setProgress(percent);
- progress.setStatus("Uploading...");
- },
- upload_success_handler : function(obj, server_data) {
- obj.id = "singlefile";
- var progress = new FileProgress(obj, this.customSettings.progress_target);
- progress.setComplete();
- progress.setStatus("Complete!");
- if (server_data == ' ') {
- this.customSettings.upload_successful = false;
- } else {
- this.customSettings.upload_successful = true;
- document.getElementById('upload_fileid').value = server_data;
- }
- },
- upload_complete_handler : function(obj) {
- if (this.customSettings.upload_successful) {
- var d = document.getElementById('update_post');
- if (d) d.disabled = 'true';
- uploadDone();
- } else {
- obj.id = 'singlefile';
- var progress = new FileProgress(obj, this.customSettings.progress_target);
- progress.setError();
- progress.setStatus("File rejected");
- document.getElementById('txtfilename').value = '';
- }
-
- },
- upload_error_handler : uploadError,
-
- swfupload_element_id : "fileupload_flashUI",
- degraded_element_id : "fileupload_normalUI",
- custom_settings : {
- upload_successful : false,
- progress_target : 'fileupload_flashUI'
- }
-};
-*/
diff --git a/web/js/fixmystreet.js b/web/js/fixmystreet.js
new file mode 100644
index 000000000..7f6014c6c
--- /dev/null
+++ b/web/js/fixmystreet.js
@@ -0,0 +1,78 @@
+/*
+ * fixmystreet.js
+ * FixMyStreet JavaScript
+ */
+
+
+YAHOO.util.Event.onContentReady('pc', function() {
+ if (this.id && this.value == this.defaultValue) {
+ this.focus();
+ }
+});
+
+YAHOO.util.Event.onContentReady('mapForm', function() {
+ this.onsubmit = function() {
+ if (this.submit_problem) {
+ this.onsubmit = function() { return false; };
+ }
+
+ /* XXX Should be in Tilma code only */
+ if (this.x) {
+ this.x.value = fixmystreet.x + 3;
+ this.y.value = fixmystreet.y + 3;
+ }
+
+ return true;
+ }
+});
+
+YAHOO.util.Event.onContentReady('another_qn', function() {
+ if (!document.getElementById('been_fixed_no').checked && !document.getElementById('been_fixed_unknown').checked) {
+ YAHOO.util.Dom.setStyle(this, 'display', 'none');
+ }
+ YAHOO.util.Event.addListener('been_fixed_no', 'click', function(e) {
+ YAHOO.util.Dom.setStyle('another_qn', 'display', 'block');
+ });
+ YAHOO.util.Event.addListener('been_fixed_unknown', 'click', function(e) {
+ YAHOO.util.Dom.setStyle('another_qn', 'display', 'block');
+ });
+ YAHOO.util.Event.addListener('been_fixed_yes', 'click', function(e) {
+ YAHOO.util.Dom.setStyle('another_qn', 'display', 'none');
+ });
+});
+
+var timer;
+function email_alert_close() {
+ YAHOO.util.Dom.setStyle('email_alert_box', 'display', 'none');
+}
+YAHOO.util.Event.onContentReady('email_alert', function() {
+ YAHOO.util.Event.addListener(this, 'click', function(e) {
+ if (!document.getElementById('email_alert_box'))
+ return true;
+ YAHOO.util.Event.preventDefault(e);
+ if (YAHOO.util.Dom.getStyle('email_alert_box', 'display') == 'block') {
+ email_alert_close();
+ } else {
+ var pos = YAHOO.util.Dom.getXY(this);
+ pos[0] -= 20; pos[1] += 20;
+ YAHOO.util.Dom.setStyle('email_alert_box', 'display', 'block');
+ YAHOO.util.Dom.setXY('email_alert_box', pos);
+ document.getElementById('alert_rznvy').focus();
+ }
+ });
+ YAHOO.util.Event.addListener(this, 'mouseout', function(e) {
+ timer = window.setTimeout(email_alert_close, 2000);
+ });
+ YAHOO.util.Event.addListener(this, 'mouseover', function(e) {
+ window.clearTimeout(timer);
+ });
+});
+YAHOO.util.Event.onContentReady('email_alert_box', function() {
+ YAHOO.util.Event.addListener(this, 'mouseout', function(e) {
+ timer = window.setTimeout(email_alert_close, 2000);
+ });
+ YAHOO.util.Event.addListener(this, 'mouseover', function(e) {
+ window.clearTimeout(timer);
+ });
+});
+
diff --git a/web/js/map-OpenLayers.js b/web/js/map-OpenLayers.js
index ed3ca4653..22492e400 100644
--- a/web/js/map-OpenLayers.js
+++ b/web/js/map-OpenLayers.js
@@ -1,6 +1,6 @@
YAHOO.util.Event.onContentReady('map', function() {
- fixmystreet.ZOOM_OFFSET = 14;
+ fixmystreet.ZOOM_OFFSET = 13;
var perm = new OpenLayers.Control.Permalink();
set_map_config(perm);
@@ -13,7 +13,7 @@ YAHOO.util.Event.onContentReady('map', function() {
fixmystreet.layer_options = OpenLayers.Util.extend({
zoomOffset: fixmystreet.ZOOM_OFFSET,
transitionEffect: 'resize',
- numZoomLevels: 4
+ numZoomLevels: 5
}, fixmystreet.layer_options);
var layer = new fixmystreet.map_type("", fixmystreet.layer_options);
fixmystreet.map.addLayer(layer);
@@ -24,7 +24,7 @@ YAHOO.util.Event.onContentReady('map', function() {
new OpenLayers.Projection("EPSG:4326"),
fixmystreet.map.getProjectionObject()
);
- fixmystreet.map.setCenter(centre, fixmystreet.zoom || 2);
+ fixmystreet.map.setCenter(centre, fixmystreet.zoom || 3);
}
if (document.getElementById('mapForm')) {
diff --git a/web/js/map-bing-ol.js b/web/js/map-bing-ol.js
index 254d407f4..00c3a487f 100644
--- a/web/js/map-bing-ol.js
+++ b/web/js/map-bing-ol.js
@@ -51,7 +51,7 @@ OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {
OpenLayers.Util.indexOf(this.serverResolutions, res) :
this.map.getZoom() + this.zoomOffset;
- if (z == 16) {
+ if (z >= 16) {
var url = [
"http://a.os.openstreetmap.org/sv/${z}/${x}/${y}.png",
"http://b.os.openstreetmap.org/sv/${z}/${x}/${y}.png",
diff --git a/web/js/map-tilma.js b/web/js/map-tilma.js
index 1b8cc6450..3c5f546e5 100644
--- a/web/js/map-tilma.js
+++ b/web/js/map-tilma.js
@@ -146,10 +146,6 @@ function load_pins(x, y) {
'y=' + (y+3),
'all_pins=' + all_pins ];
- if (document.getElementById('extra_param')) {
- ajax_params.push(document.getElementById('extra_param').name + '=' + document.getElementById('extra_param').value);
- }
-
var url = [ root_path , '/ajax?', ajax_params.join(';')].join('');
YAHOO.util.Connect.asyncRequest('GET', url, {
success: pins_loaded
diff --git a/web/json.cgi b/web/json.cgi
deleted file mode 100755
index 512750988..000000000
--- a/web/json.cgi
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# json.cgi:
-# A small JSON API for FixMyStreet
-#
-# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
-# Email: louise@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: json.cgi,v 1.4 2010-01-20 11:31:26 matthew Exp $
-
-use strict;
-use Error qw(:try);
-use JSON;
-use Standard;
-
-sub main {
- my $q = shift;
- my $problems;
- my $type = $q->param('type') || '';
- my $start_date = $q->param('start_date') || '';
- my $end_date = $q->param('end_date') || '';
- if ($start_date !~ /^\d{4}-\d\d-\d\d$/ || $end_date !~ /^\d{4}-\d\d-\d\d$/) {
- $problems = { error => 'Invalid dates supplied' };
- } elsif ($type eq 'new_problems') {
- $problems = Problems::created_in_interval($start_date, $end_date);
- } elsif ($type eq 'fixed_problems') {
- $problems = Problems::fixed_in_interval($start_date, $end_date);
- }
- print $q->header( -type => 'application/json; charset=utf-8' );
- print JSON::to_json($problems);
-}
-
-
-Page::do_fastcgi(\&main);
-
diff --git a/web/photo.cgi b/web/photo.cgi
deleted file mode 100755
index bd38e3bf1..000000000
--- a/web/photo.cgi
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# photo.cgi:
-# Display a photo for FixMyStreet
-#
-# Copyright (c) 2006 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: photo.cgi,v 1.11 2008-10-09 14:20:54 matthew Exp $
-
-use strict;
-use Standard;
-use Error qw(:try);
-use CGI::Carp;
-
-sub main {
- my $q = shift;
- print $q->header(-type => 'image/jpeg',
- -expires => '+1y' );
- my $id = $q->param('id');
- my $c = $q->param('c');
- return unless ($id || $c);
- my $photo;
- if ($c) {
- $photo = dbh()->selectrow_arrayref("select photo from comment where
- id=? and state = 'confirmed' and photo is not null", {}, $c);
- } else {
- $photo = dbh()->selectrow_arrayref( "select photo from problem where
- id=? and state in ('confirmed', 'fixed', 'partial') and photo is not
- null", {}, $id);
- }
- return unless $photo;
- $photo = $photo->[0];
- if ($q->param('tn')) {
- $photo = resize($photo, 'x100');
- } elsif ($q->{site} eq 'emptyhomes') {
- $photo = resize($photo, '195x');
- }
-
- print $photo;
-}
-Page::do_fastcgi(\&main, 0, 1);
-
-sub resize {
- my ($photo, $size) = @_;
- use Image::Magick;
- my $image = Image::Magick->new;
- $image->BlobToImage($photo);
- my $err = $image->Scale(geometry => "$size>");
- throw Error::Simple("resize failed: $err") if "$err";
- my @blobs = $image->ImageToBlob();
- undef $image;
- return $blobs[0];
-}
diff --git a/web/posters/index.cgi b/web/posters/index.cgi
deleted file mode 100755
index f26252131..000000000
--- a/web/posters/index.cgi
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/perl -w -I../../perllib -I../../commonlib/perllib
-
-# posters/index.cgi:
-# List of publicity stuff on FixMyStreet
-#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: index.cgi,v 1.10 2008-10-17 20:19:05 matthew Exp $
-
-use strict;
-use Standard -db;
-use mySociety::Config;
-use mySociety::Web qw(ent);
-
-# XXX: Ugh, as we're in a subdirectory
-BEGIN {
- mySociety::Config::set_file("$FindBin::Bin/../../conf/general");
-}
-
-sub main {
- my $q = shift;
- print Page::header($q, title=>_('Publicity material'));
- print body($q);
- print Page::footer($q);
-}
-Page::do_fastcgi(\&main);
-
-sub body {
- my $q = shift;
- my $badge = '<a href="http://www.fixmystreet.com/"> <img align="left" hspace="5" src="http://www.fixmystreet.com/i/fms-badge.jpeg" alt="FixMyStreet - report, view or discuss local problems" border="0"></a>';
- return $q->h1(_('Publicity Material')) .
- $q->div({style=>'float:left; width:50%'},
- '<p>Copy and paste the text below to add this badge to your site:</p>', $badge,
- '<textarea onclick="this.select()" cols=37 rows=5>' . ent($badge) . '</textarea>',
- '<p><small>(thanks to Lincolnshire Council for the image)</small></p>'
- ) .
- $q->div({style=>'float:right; width:47%'},
- $q->p(_('Here are some posters and flyers you can use to publicise FixMyStreet:')) .
- '<img hspace="5" src="poster.png" alt="Example poster">' .
- $q->h2(_('Posters')) .
- $q->ul(
- $q->li($q->a({href=>'fixmystreet-poster-a4.pdf'}, _('A4, colour'))),
- $q->li($q->a({href=>'fixmystreet-poster-a4-bw.pdf'}, _('A4, black and white'))),
- $q->li($q->a({href=>'fixmystreet-poster-a4-bw-low-ink.pdf'}, _('A4, black and white, low ink'))),
- $q->li($q->a({href=>'fixmystreet-poster-a4-bw-outlined.pdf'}, _('A4, black and white, outlined'))),
- ) .
- $q->h2(_('Posters with tags')) .
- $q->ul(
- $q->li($q->a({href=>'fixmystreet-poster-tags.pdf'}, _('A4, colour'))) .
- $q->li($q->a({href=>'fixmystreet-poster-tags-bw.pdf'}, _('A4, black and white'))) .
- $q->li($q->a({href=>'fixmystreet-poster-tags-bw-low-ink.pdf'}, _('A4, black and white, low ink'))) .
- $q->li($q->a({href=>'fixmystreet-poster-tags-only.pdf'}, _('A4, tags only')))
- ) .
- $q->h2(_('Flyers')) .
- $q->ul(
- $q->li($q->a({href=>'fixmystreet-flyers-colour.pdf'}, _('4 x A6, colour'))),
- $q->li($q->a({href=>'fixmystreet-flyers-bw-outlined.pdf'}, _('4 x A6, black and white, outlined'))),
- $q->li($q->a({href=>'fixmystreet-flyers-bw-low-ink.pdf'}, _('4 x A6, black and white, low ink')))
- )
- )
- ;
-}
-
diff --git a/web/questionnaire.cgi b/web/questionnaire.cgi
deleted file mode 100755
index fdb1c08a4..000000000
--- a/web/questionnaire.cgi
+++ /dev/null
@@ -1,338 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# questionnaire.cgi:
-# Questionnaire for problem creators
-#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: questionnaire.cgi,v 1.53 2009-12-08 17:43:13 louise Exp $
-
-use strict;
-use Standard;
-use Utils;
-use Error qw(:try);
-use CrossSell;
-use mySociety::AuthToken;
-use mySociety::Locale;
-use mySociety::Web qw(ent);
-
-sub main {
- my $q = shift;
- my $out = '';
- if ($q->param('submit')) {
- $out = submit_questionnaire($q);
- } else {
- $out = display_questionnaire($q);
- }
- print Page::header($q,
- title => _('Questionnaire'),
- js => FixMyStreet::Map::header_js(),
- );
- print $out;
- print Page::footer($q);
-}
-Page::do_fastcgi(\&main);
-
-sub check_stuff {
- my $q = shift;
- my $cobrand = Page::get_cobrand($q);
- my $id = mySociety::AuthToken::retrieve('questionnaire', $q->param('token'));
- throw Error::Simple(_("I'm afraid we couldn't validate that token. If you've copied the URL from an email, please check that you copied it exactly.\n")) unless $id;
-
- my $questionnaire = dbh()->selectrow_hashref(
- 'select id, problem_id, whenanswered from questionnaire where id=?', {}, $id);
- my $problem_id = $questionnaire->{problem_id};
- my $problem_url = Cobrand::url($cobrand, "/report/$problem_id", $q);
- my $contact_url = Cobrand::url($cobrand, "/contact", $q);
- throw Error::Simple(sprintf(_("You have already answered this questionnaire. If you have a question, please <a href='%s'>get in touch</a>, or <a href='%s'>view your problem</a>.\n"), $contact_url, $problem_url)) if $questionnaire->{whenanswered};
-
- my $problem = dbh()->selectrow_hashref(
- "select *, extract(epoch from confirmed) as time, extract(epoch from whensent-confirmed) as whensent
- from problem where id=? and state in ('confirmed','fixed')", {}, $problem_id);
- throw Error::Simple(_("I'm afraid we couldn't locate your problem in the database.\n")) unless $problem;
-
- my $num_questionnaire = dbh()->selectrow_array(
- 'select count(*) from questionnaire where problem_id=?', {}, $problem_id);
- my $answered_ever_reported = dbh()->selectrow_array(
- 'select id from questionnaire where problem_id in (select id from problem where email=?) and ever_reported is not null', {}, $problem->{email});
-
- return ($questionnaire, $problem, $num_questionnaire, $answered_ever_reported);
-}
-
-sub submit_questionnaire {
- my $q = shift;
- my $cobrand = Page::get_cobrand($q);
- my $cobrand_data = Cobrand::extra_data($cobrand, $q);
- my @vars = qw(token id been_fixed reported update another);
- my %input = map { $_ => scalar $q->param($_) || '' } @vars;
- my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
-
- my ($error, $questionnaire, $num_questionnaire, $problem, $answered_ever_reported);
- try {
- ($questionnaire, $problem, $num_questionnaire, $answered_ever_reported) = check_stuff($q);
- } catch Error::Simple with {
- my $e = shift;
- $error = $e;
- };
-
- if ($error) {
- my %vars = (heading => _('Questionnaire'),
- error => $error->stringify());
- my $template_error = Page::template_include('error', $q, Page::template_root($q), %vars);
- return $template_error if $template_error;
- return $error;
- }
- # EHA questionnaires done for you
- if ($q->{site} eq 'emptyhomes') {
- $input{another} = $num_questionnaire==1 ? 'Yes' : 'No';
- }
-
- my @errors;
- push @errors, _('Please state whether or not the problem has been fixed') unless $input{been_fixed};
- my $ask_ever_reported = Cobrand::ask_ever_reported($cobrand);
- if ($ask_ever_reported) {
- push @errors, _('Please say whether you\'ve ever reported a problem to your council before') unless $input{reported} || $answered_ever_reported;
- }
- push @errors, _('Please indicate whether you\'d like to receive another questionnaire')
- if ($input{been_fixed} eq 'No' || $input{been_fixed} eq 'Unknown') && !$input{another};
- push @errors, _('Please provide some explanation as to why you\'re reopening this report')
- if $input{been_fixed} eq 'No' && $problem->{state} eq 'fixed' && !$input{update};
- return display_questionnaire($q, @errors) if @errors;
-
- my $fh = $q->upload('photo');
- my $image;
- if ($fh) {
- my $err = Page::check_photo($q, $fh);
- push @errors, $err if $err;
- try {
- $image = Page::process_photo($fh) unless $err;
- } catch Error::Simple with {
- my $e = shift;
- push(@errors, "That image doesn't appear to have uploaded correctly ($e), please try again.");
- };
- }
- push @errors, _('Please provide some text as well as a photo')
- if $image && !$input{update};
- return display_questionnaire($q, @errors) if @errors;
-
- my $new_state = '';
- $new_state = 'fixed' if $input{been_fixed} eq 'Yes' && $problem->{state} eq 'confirmed';
- $new_state = 'confirmed' if $input{been_fixed} eq 'No' && $problem->{state} eq 'fixed';
-
- # Record state change, if there was one
- dbh()->do("update problem set state=?, lastupdate=ms_current_timestamp()
- where id=?", {}, $new_state, $problem->{id})
- if $new_state;
-
- # If it's not fixed and they say it's still not been fixed, record time update
- dbh()->do("update problem set lastupdate=ms_current_timestamp()
- where id=?", {}, $problem->{id})
- if $input{been_fixed} eq 'No' && $problem->{state} eq 'confirmed';
-
- # Record questionnaire response
- my $reported = $input{reported}
- ? ($input{reported} eq 'Yes' ? 't' : ($input{reported} eq 'No' ? 'f' : undef))
- : undef;
- dbh()->do('update questionnaire set whenanswered=ms_current_timestamp(),
- ever_reported=?, old_state=?, new_state=? where id=?', {},
- $reported, $problem->{state}, $input{been_fixed} eq 'Unknown'
- ? 'unknown'
- : ($new_state ? $new_state : $problem->{state}),
- $questionnaire->{id});
-
- # Record an update if they've given one, or if there's a state change
- my $name = $problem->{anonymous} ? undef : $problem->{name};
- my $update = $input{update} ? $input{update} : _('Questionnaire filled in by problem reporter');
- Utils::workaround_pg_bytea("insert into comment
- (problem_id, name, email, website, text, state, mark_fixed, mark_open, photo, lang, cobrand, cobrand_data, confirmed)
- values (?, ?, ?, '', ?, 'confirmed', ?, ?, ?, ?, ?, ?, ms_current_timestamp())", 7,
- $problem->{id}, $name, $problem->{email}, $update,
- $new_state eq 'fixed' ? 't' : 'f', $new_state eq 'confirmed' ? 't' : 'f',
- $image, $mySociety::Locale::lang, $cobrand, $cobrand_data
- )
- if $new_state || $input{update};
-
- # If they've said they want another questionnaire, mark as such
- dbh()->do("update problem set send_questionnaire = 't' where id=?", {}, $problem->{id})
- if ($input{been_fixed} eq 'No' || $input{been_fixed} eq 'Unknown') && $input{another} eq 'Yes';
- dbh()->commit();
-
- my $out;
- my $message;
- my $advert_outcome = 1;
- if ($input{been_fixed} eq 'Unknown') {
- $message = _(<<EOF);
-<p>Thank you very much for filling in our questionnaire; if you
-get some more information about the status of your problem, please come back to the
-site and leave an update.</p>
-EOF
- } elsif ($new_state eq 'confirmed' || (!$new_state && $problem->{state} eq 'confirmed')) {
- my $wtt_url = Cobrand::writetothem_url($cobrand, $cobrand_data);
- $wtt_url = "http://www.writetothem.com" if (! $wtt_url);
- $message = sprintf(_(<<EOF), $wtt_url);
-<p style="font-size:150%%">We're sorry to hear that. We have two suggestions: why not try
-<a href="%s">writing direct to your councillor(s)</a>
-or, if it's a problem that could be fixed by local people working together,
-why not <a href="http://www.pledgebank.com/new">make and publicise a pledge</a>?
-</p>
-EOF
- $advert_outcome = 0;
- } else {
- $message = _(<<EOF);
-<p style="font-size:150%">Thank you very much for filling in our questionnaire; glad to hear it's been fixed.</p>
-EOF
- }
- $out = $message;
- my $display_advert = Cobrand::allow_crosssell_adverts($cobrand);
- if ($display_advert && $advert_outcome) {
- $out .= CrossSell::display_advert($q, $problem->{email}, $problem->{name},
- council => $problem->{council});
- }
- my %vars = (message => $message);
- my $template_page = Page::template_include('questionnaire-completed', $q, Page::template_root($q), %vars);
- return $template_page if ($template_page);
- return $out;
-}
-
-sub display_questionnaire {
- my ($q, @errors) = @_;
- my @vars = qw(token id been_fixed reported update another);
- my $cobrand = Page::get_cobrand($q);
- my %input = map { $_ => $q->param($_) || '' } @vars;
- my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
-
- my ($error, $questionnaire, $num_questionnaire, $problem, $answered_ever_reported);
- try {
- ($questionnaire, $problem, $num_questionnaire, $answered_ever_reported) = check_stuff($q);
- } catch Error::Simple with {
- my $e = shift;
- $error = $e;
- };
- if ($error) {
- my %vars = (heading => _('Questionnaire'),
- error => $error->stringify());
- my $template_error = Page::template_include('error', $q, Page::template_root($q), %vars);
- return $template_error if $template_error;
- return $error;
- }
- my $reported_date_time = Page::prettify_epoch($q, $problem->{time});
- my $problem_text = Page::display_problem_text($q, $problem);
- my $updates = Page::display_problem_updates($problem->{id}, $q);
-
- my %vars = (
- input_h => \%input_h,
- map_start => FixMyStreet::Map::display_map($q,
- latitude => $problem->{latitude}, longitude => $problem->{longitude},
- pins => [
- [ $problem->{latitude}, $problem->{longitude}, $problem->{state} eq 'fixed'?'green':'red' ],
- ],
- pre => $problem_text, post => $updates
- ),
- map_end => FixMyStreet::Map::display_map_end(0),
- heading => _('Questionnaire'),
- yes => _('Yes'),
- no => _('No'),
- dontknow => _('Don&rsquo;t know'),
- submit => _('Submit questionnaire'),
- cobrand_form_elements => Cobrand::form_elements($cobrand, 'questionnaireForm', $q),
- form_action => Cobrand::url($cobrand, "/questionnaire", $q),
- reported_date_time => $reported_date_time
- );
- $vars{been_fixed} = {
- yes => $input{been_fixed} eq 'Yes' ? ' checked' : '',
- no => $input{been_fixed} eq 'No' ? ' checked' : '',
- unknown => $input{been_fixed} eq 'Unknown' ? ' checked' : '',
- };
- my $allow_photo_upload = Cobrand::allow_photo_upload($cobrand);
- if ($allow_photo_upload) {
- $vars{enctype} = 'enctype="multipart/form-data"';
- }
- if ($q->{site} eq 'emptyhomes') {
- if ($num_questionnaire==1) {
- $vars{blurb_eh} = _(<<EOF);
-<p>Getting empty homes back into use can be difficult. You shouldn't expect
-the property to be back into use yet. But a good council will have started work
-and should have reported what they have done on the website. If you are not
-satisfied with progress or information from the council, now is the right time
-to say. You may also want to try contacting some other people who may be able
-to help. For advice on how to do this and other useful information please
-go to <a href="http://www.emptyhomes.com/getinvolved/campaign.html">http://www.emptyhomes.com/getinvolved/campaign.html</a>.</p>
-EOF
- } else {
- $vars{blurb_eh} = _(<<EOF);
-<p>Getting empty homes back into use can be difficult, but by now a good council
-will have made a lot of progress and reported what they have done on the
-website. Even so properties can remain empty for many months if the owner is
-unwilling or the property is in very poor repair. If nothing has happened or
-you are not satisfied with the progress the council is making, now is the right
-time to say so. We think it's a good idea to contact some other people who
-may be able to help or put pressure on the council For advice on how to do
-this and other useful information please go to <a
-href="http://www.emptyhomes.com/getinvolved/campaign.html">http://www.emptyhomes.com/getinvolved/campaign.html</a>.</p>
-EOF
- }
- }
-
- $vars{blurb_report} = _('The details of your problem are available on the right hand side of this page.');
- $vars{blurb_report2} = _('Please take a look at the updates that have been left.') if $updates;
-
- if (@errors) {
- $vars{errors} = '<ul class="error"><li>' . join('</li><li>', @errors) . '</li></ul>';
- }
- $vars{fixed_question} = '';
- $vars{fixed_question} .= _('An update marked this problem as fixed.') . ' ' if $problem->{state} eq 'fixed';
- $vars{fixed_question} .= _('Has this problem been fixed?') . '</p>';
-
- unless ($answered_ever_reported) {
- my %reported = (
- yes => $input{reported} eq 'Yes' ? ' checked' : '',
- no => $input{reported} eq 'No' ? ' checked' : '',
- );
- my $before = _('Reported before');
- my $first = _('First time');
- $vars{ever_reported} = $q->p(_('Have you ever reported a problem to a council before, or is this your first time?'));
- $vars{ever_reported} .= <<EOF;
-<p>
-<input type="radio" name="reported" id="reported_yes" value="Yes"$reported{yes}>
-<label for="reported_yes">$before</label>
-<input type="radio" name="reported" id="reported_no" value="No"$reported{no}>
-<label for="reported_no">$first</label>
-</p>
-EOF
- }
- $vars{blurb_update} = $q->p(_('If you wish to leave a public update on the problem, please enter it here
-(please note it will not be sent to the council). For example, what was
-your experience of getting the problem fixed?'));
- if ($allow_photo_upload) {
- my $photo = _('Photo:');
- $vars{photo_input} = <<EOF;
-<div id="fileupload_normalUI">
-<label for="form_photo">$photo</label>
-<input type="file" name="photo" id="form_photo">
-</div>
-EOF
- }
- my %another = (
- yes => $input{another} eq 'Yes' ? ' checked' : '',
- no => $input{another} eq 'No' ? ' checked' : '',
- );
- $vars{another_yes} = $another{yes};
- $vars{another_no} = $another{no};
- my $another_qn = _('Would you like to receive another questionnaire in 4 weeks, reminding you to check the status?');
- my $yes = _('Yes');
- my $no = _('No');
- $vars{another_questionnaire} = <<EOF if $q->{site} ne 'emptyhomes';
-<div id="another_qn">
-<p>$another_qn</p>
-<p>
-<input type="radio" name="another" id="another_yes" value="Yes"$another{yes}>
-<label for="another_yes">$yes</label>
-<input type="radio" name="another" id="another_no" value="No"$another{no}>
-<label for="another_no">$no</label>
-</p>
-</div>
-EOF
-
- return Page::template_include('questionnaire', $q, Page::template_root($q), %vars);
-}
diff --git a/web/reports.cgi b/web/reports.cgi
deleted file mode 100755
index 22dbe344a..000000000
--- a/web/reports.cgi
+++ /dev/null
@@ -1,341 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# report.cgi:
-# Display summary reports for FixMyStreet
-# And RSS feeds for those reports etc.
-#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: reports.cgi,v 1.41 2009-12-08 11:13:30 louise Exp $
-
-use strict;
-use Standard;
-use Encode;
-use POSIX qw(strcoll);
-use URI::Escape;
-use FixMyStreet::Alert;
-use mySociety::MaPit;
-use mySociety::Web qw(ent NewURL);
-use mySociety::VotingArea;
-
-sub main {
- my $q = shift;
- my $all = $q->param('all') || 0;
- my $rss = $q->param('rss') || '';
- my $cobrand = Page::get_cobrand($q);
-
- # Look up council name, if given
- my $q_council = $q->param('council') || '';
- my $base_url = Cobrand::base_url($cobrand);
-
- # Manual misspelling redirect
- if ($q_council =~ /^rhondda cynon taff$/i) {
- print $q->redirect($base_url . '/reports/Rhondda+Cynon+Taf');
- return;
- }
-
- my ($one_council, $area_type, $area_name);
- if ($q_council =~ /^(\d\d)([a-z]{2})?([a-z]{2})?$/i) {
- my $va_info = mySociety::MaPit::call('area', uc $q_council);
- if ($va_info->{error}) { # Given a bad/old ONS code
- print $q->redirect($base_url . '/reports');
- return;
- }
- $area_name = Page::short_name($va_info);
- if (length($q_council) == 6) {
- $va_info = mySociety::MaPit::call('area', $va_info->{parent_area});
- $area_name = Page::short_name($va_info) . '/' . $area_name;
- }
- $rss = '/rss' if $rss;
- print $q->redirect($base_url . $rss . '/reports/' . $area_name);
- return;
- } elsif (mySociety::Config::get('COUNTRY') eq 'NO' && $q_council eq 'Oslo') {
- $one_council = mySociety::MaPit::call('area', 3);
- $area_type = $one_council->{type};
- $area_name = $one_council->{name};
- } elsif (mySociety::Config::get('COUNTRY') eq 'NO' && $q_council =~ /,/) {
- my ($kommune, $fylke) = split /\s*,\s*/, $q_council;
- my @area_types = Cobrand::area_types($cobrand);
- my $areas_k = mySociety::MaPit::call('areas', $kommune, type => \@area_types);
- my $areas_f = mySociety::MaPit::call('areas', $fylke, type => \@area_types);
- if (keys %$areas_f == 1) {
- ($fylke) = values %$areas_f;
- foreach (values %$areas_k) {
- if ($_->{name} eq $kommune && $_->{parent_area} == $fylke->{id}) {
- $one_council = $_;
- $area_type = $_->{type};
- $area_name = $_->{name};
- last;
- }
- }
- }
- if (!$one_council) { # Given a false council name
- print $q->redirect($base_url . '/reports');
- return;
- }
- } elsif ($q_council =~ /\D/) {
- my @area_types = Cobrand::area_types($cobrand);
- my $areas = mySociety::MaPit::call('areas', $q_council, type => \@area_types, min_generation=>Cobrand::area_min_generation($cobrand) );
- if (keys %$areas == 1) {
- ($one_council) = values %$areas;
- $area_type = $one_council->{type};
- $area_name = $one_council->{name};
- } else {
- foreach (keys %$areas) {
- if ($areas->{$_}->{name} eq $q_council || $areas->{$_}->{name} =~ /^\Q$q_council\E (Borough|City|District|County) Council$/) {
- $one_council = $areas->{$_};
- $area_type = $areas->{$_}->{type};
- $area_name = $q_council;
- }
- }
- }
- if (!$one_council) { # Given a false council name
- print $q->redirect($base_url . '/reports');
- return;
- }
- } elsif ($q_council =~ /^\d+$/) {
- my $va_info = mySociety::MaPit::call('area', $q_council);
- if ($va_info->{error}) {
- print $q->redirect($base_url . '/reports');
- return;
- }
- print $q->redirect($base_url . '/reports/' . Page::short_name($va_info));
- return;
- }
- $all = 0 unless $one_council;
-
- # Look up ward name, if given
- my $q_ward = $q->param('ward') || '';
- my $ward;
- if ($one_council && $q_ward) {
- my $qw = mySociety::MaPit::call('areas', $q_ward, type => $mySociety::VotingArea::council_child_types,
- min_generation => Cobrand::area_min_generation($cobrand));
- foreach my $id (sort keys %$qw) {
- if ($qw->{$id}->{parent_area} == $one_council->{id}) {
- $ward = $qw->{$id};
- last;
- }
- }
- if (!$ward) { # Given a false ward name
- print $q->redirect($base_url . '/reports/' . Page::short_name($one_council));
- return;
- }
- }
-
- # RSS - reports for sent reports, area for all problems in area
- if ($rss && $one_council) {
- my $url = Page::short_name($one_council);
- $url .= '/' . Page::short_name($ward) if $ward;
- if ($rss eq 'area' && $area_type ne 'DIS' && $area_type ne 'CTY') {
- # Two possibilites are the same for one-tier councils, so redirect one to the other
- print $q->redirect($base_url . '/rss/reports/' . $url);
- return;
- }
- my $type = 'council_problems'; # Problems sent to a council
- my (@params, %title_params);
- $title_params{COUNCIL} = $area_name;
- push @params, $one_council->{id} if $rss eq 'reports';
- push @params, $ward ? $ward->{id} : $one_council->{id};
- if ($ward && $rss eq 'reports') {
- $type = 'ward_problems'; # Problems sent to a council, restricted to a ward
- $title_params{WARD} = $q_ward;
- } elsif ($rss eq 'area') {
- $title_params{NAME} = $ward ? $q_ward : $q_council;
- $type = 'area_problems'; # Problems within an area
- }
- print $q->header( -type => 'application/xml; charset=utf-8' );
- my $xsl = Cobrand::feed_xsl($cobrand);
- my $out = FixMyStreet::Alert::generate_rss($type, $xsl, "/$url", \@params, \%title_params, $cobrand, $q);
- $out =~ s/matthew.fixmystreet/emptyhomes.matthew.fixmystreet/g if $q->{site} eq 'emptyhomes';
- print $out;
- return;
- }
-
- my $areas_info;
- if ($one_council) {
- $areas_info = mySociety::MaPit::call('areas', [ $one_council->{id}, $one_council->{parent_area} ])
- if $one_council->{parent_area};
- $areas_info = { $one_council->{id} => $one_council }
- unless $areas_info;
- } else {
- # Show all councils on main report page
- my @area_types = Cobrand::area_types($cobrand);
- $areas_info = mySociety::MaPit::call('areas', \@area_types, min_generation=>Cobrand::area_min_generation($cobrand) );
- }
-
- my $problems = Problems::council_problems(
- $ward ? $ward->{id} : undef,
- $one_council ? $one_council->{id} : undef
- );
-
- my (%fixed, %open);
- my $re_councils = join('|', keys %$areas_info);
- foreach my $row (@$problems) {
- if (!$row->{council}) {
- # Problem was not sent to any council, add to possible councils
- while ($row->{areas} =~ /,($re_councils)(?=,)/g) {
- add_row($row, 0, $1, \%fixed, \%open);
- }
- } else {
- # Add to councils it was sent to
- $row->{council} =~ s/\|.*$//;
- my @council = split /,/, $row->{council};
- foreach (@council) {
- next if $one_council && $_ != $one_council->{id};
- add_row($row, scalar @council, $_, \%fixed, \%open);
- }
- }
- }
-
- if (!$one_council) {
- print Page::header($q, title=>_('Summary reports'), expires=>'+1h');
- print $q->p(
- _('This is a summary of all reports on this site; select a particular council to see the reports sent there.'), ' ',
- _('Greyed-out lines are councils that no longer exist.')
- );
- my $c = 0;
- print '<table cellpadding="3" cellspacing="1" border="0">';
- print '<tr><th>' . _('Name') . '</th><th>' . _('New problems') . '</th><th>' . _('Older problems') . '</th>';
- if ($q->{site} ne 'emptyhomes') {
- print '<th>' . _('Old problems,<br>state unknown') . '</th>';
- }
- print '<th>' . _('Recently fixed') . '</th><th>' . _('Older fixed') . '</th></tr>';
- foreach (sort { strcoll($areas_info->{$a}->{name}, $areas_info->{$b}->{name}) } keys %$areas_info) {
- next if mySociety::Config::get('COUNTRY') eq 'NO' && $_ eq 301; # Only want one Oslo
- print '<tr align="center"';
- ++$c;
- if (mySociety::Config::get('COUNTRY') eq 'GB' && $areas_info->{$_}->{generation_high} == 10) {
- print ' class="gone"';
- } elsif ($c%2) {
- print ' class="a"';
- }
- my $url = Page::short_name($areas_info->{$_}, $areas_info);
- my $cobrand_url = Cobrand::url($cobrand, "/reports/$url", $q);
- print '><td align="left"><a href="' . $cobrand_url . '">' .
- $areas_info->{$_}->{name};
- if ($areas_info->{$_}->{parent_area} && $url =~ /,|%2C/) {
- print ', ' . $areas_info->{$areas_info->{$_}->{parent_area}}->{name};
- }
- print '</a></td>';
- summary_cell(\@{$open{$_}{new}});
- if ($q->{site} eq 'emptyhomes') {
- my $c = 0;
- $c += @{$open{$_}{older}} if $open{$_}{older};
- $c += @{$open{$_}{unknown}} if $open{$_}{unknown};
- summary_cell($c);
- } else {
- summary_cell(\@{$open{$_}{older}});
- summary_cell(\@{$open{$_}{unknown}});
- }
- summary_cell(\@{$fixed{$_}{new}});
- summary_cell(\@{$fixed{$_}{old}});
- print "</tr>\n";
- }
- print '</table>';
- } else {
- my $name = $one_council->{name};
- if (!$name) {
- print Page::header($q, title=>_("Summary reports"));
- print "Council with identifier " . ent($one_council->{id}). " not found. ";
- print $q->a({href => Cobrand::url($cobrand, '/reports', $q) }, 'Show all councils');
- print ".";
- } else {
- my $rss_url = '/rss/reports/' . Page::short_name($one_council, $areas_info);
- my $thing = _('council');
- if ($ward) {
- $rss_url .= '/' . Page::short_name($ward);
- $thing = 'ward';
- $name = ent($q_ward) . ", $name";
- }
- my $all_councils_report = Cobrand::all_councils_report($cobrand);
-
- my %vars = (
- rss_title => _('RSS feed'),
- rss_alt => sprintf(_('RSS feed of problems in this %s'), $thing),
- rss_url => Cobrand::url($cobrand, $rss_url, $q),
- url_home => Cobrand::url($cobrand, '/', $q),
- summary_title => $all_councils_report
- ? sprintf(_('This is a summary of all reports for one %s.'), $thing)
- : sprintf(_('This is a summary of all reports for this %s.'), $thing),
- name => $name,
- );
- if ($all && ! $all_councils_report) {
- $vars{summary_line} = sprintf(_('You can <a href="%s">see less detail</a>.'), Cobrand::url($cobrand, NewURL($q), $q));
- } elsif (! $all_councils_report) {
- $vars{summary_line} = sprintf(_('You can <a href="%s">see more details</a>.'), Cobrand::url($cobrand, NewURL($q, all=>1), $q));
- } elsif ($all) {
- $vars{summary_line} = sprintf(_('You can <a href="%s">see less detail</a> or go back and <a href="/reports">show all councils</a>.'), Cobrand::url($cobrand, NewURL($q), $q));
- } else {
- $vars{summary_line} = sprintf(_('You can <a href="%s">see more details</a> or go back and <a href="/reports">show all councils</a>.'), Cobrand::url($cobrand, NewURL($q, all=>1), $q));
- }
-
- my $id = $one_council->{id};
- if ($open{$id}) {
- my $col = list_problems($q, _('New problems'), $open{$id}{new}, $all, 0);
- my $old = [];
- if ($q->{site} eq 'emptyhomes') {
- push @$old, @{$open{$id}{older}} if $open{$id}{older};
- push @$old, @{$open{$id}{unknown}} if $open{$id}{unknown};
- } else {
- $old = $open{$id}{older};
- }
- $col .= list_problems($q, _('Older problems'), $old, $all, 0);
- if ($q->{site} ne 'emptyhomes') {
- $col .= list_problems($q, _('Old problems, state unknown'), $open{$id}{unknown}, $all, 0);
- }
- $vars{col_problems} = $col;
- }
- if ($fixed{$id}) {
- my $col = list_problems($q, _('Recently fixed'), $fixed{$id}{new}, $all, 1);
- $col .= list_problems($q, _('Old fixed'), $fixed{$id}{old}, $all, 1);
- $vars{col_fixed} = $col;
- }
- print Page::header($q, context => 'reports', title=>sprintf(_('%s - Summary reports'), $name), rss => [ sprintf(_('Problems within %s, FixMyStreet'), $name), Cobrand::url($cobrand, $rss_url, $q) ]);
- print Page::template_include('reports', $q, Page::template_root($q), %vars);
- }
- }
- print Page::footer($q);
-}
-Page::do_fastcgi(\&main);
-
-sub add_row {
- my ($row, $councils, $council, $fixed, $open) = @_;
- my $fourweeks = 4*7*24*60*60;
- my $duration = ($row->{duration} > 2 * $fourweeks) ? 'old' : 'new';
- my $type = ($row->{duration} > 2 * $fourweeks)
- ? 'unknown'
- : ($row->{age} > $fourweeks ? 'older' : 'new');
- $row->{councils} = $councils;
- #Fixed problems are either old or new
- push @{$fixed->{$council}{$duration}}, $row if $row->{state} eq 'fixed';
- # Open problems are either unknown, older, or new
- push @{$open->{$council}{$type}}, $row if $row->{state} eq 'confirmed';
-}
-
-sub summary_cell {
- my $c = shift;
- $c = 0 unless defined $c;
- $c = @$c if ref($c) eq 'ARRAY';
- print '<td>' . $c . '</td>';
-}
-
-sub list_problems {
- my ($q, $title, $problems, $all, $fixed) = @_;
- return '' unless $problems;
- my $cobrand = Page::get_cobrand($q);
- my $out = "<h3>$title</h3>\n<ul>";
- foreach (sort { $fixed ? ($a->{duration} <=> $b->{duration}) : ($a->{age} <=> $b->{age}) } @$problems) {
- my $url = Cobrand::url($cobrand, "/report/" . $_->{id}, $q);
- $out .= '<li><a href="' . $url . '">';
- $out .= ent($_->{title});
- $out .= '</a>';
- $out .= ' <small>' . _('(sent to both)') . '</small>' if $_->{councils}>1;
- $out .= ' <small>' . _('(not sent to council)') . '</small>' if $_->{councils}==0 && $q->{site} ne 'emptyhomes';
- $out .= '<br><small>' . ent($_->{detail}) . '</small>' if $all;
- $out .= '</li>';
- }
- $out .= '</ul>';
- return $out;
-}
-
diff --git a/web/rss.cgi b/web/rss.cgi
deleted file mode 100755
index 1570ca97f..000000000
--- a/web/rss.cgi
+++ /dev/null
@@ -1,169 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# rss.cgi:
-# RSS for FixMyStreet
-#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: rss.cgi,v 1.38 2009-12-17 15:15:21 louise Exp $
-
-use strict;
-use Error qw(:try);
-use Standard;
-use Encode;
-use URI::Escape;
-use FixMyStreet::Alert;
-use FixMyStreet::Geocode;
-use mySociety::Locale;
-use mySociety::MaPit;
-use mySociety::Gaze;
-use Utils;
-
-sub main {
- my $q = shift;
- my $type = $q->param('type') || '';
- my $cobrand = Page::get_cobrand($q);
- my $xsl = Cobrand::feed_xsl($cobrand);
- my $out;
- if ($type eq 'local_problems') {
- $out = rss_local_problems($q);
- return unless $out;
- } elsif ($type eq 'new_updates') {
- my $id = $q->param('id');
- my $problem = Problems::fetch_problem($id);
- if (!$problem) {
- print $q->header(-status=>'404 Not Found',-type=>'text/html');
- return;
- }
- my $qs = 'report/' . $id;
- $out = FixMyStreet::Alert::generate_rss($type, $xsl, $qs, [$id], undef, $cobrand, $q);
- } elsif ($type eq 'new_problems' || $type eq 'new_fixed_problems') {
- $out = FixMyStreet::Alert::generate_rss($type, $xsl, '', undef, undef, $cobrand, $q);
- } elsif ($type eq 'council_problems') {
- my $id = $q->param('id');
- my $qs = '/'.$id;
- $out = FixMyStreet::Alert::generate_rss($type, $xsl, $qs, [$id], undef, $cobrand. $q);
- } elsif ($type eq 'area_problems') {
- my $id = $q->param('id');
- my $va_info = mySociety::MaPit::call('area', $id);
- my $qs = '/'.$id;
- $out = FixMyStreet::Alert::generate_rss($type, $xsl, $qs, [$id], { NAME => $va_info->{name} }, $cobrand, $q);
- } elsif ($type eq 'all_problems') {
- $out = FixMyStreet::Alert::generate_rss($type, $xsl, '', undef, undef, $cobrand, $q);
- } else {
- my $base = mySociety::Config::get('BASE_URL');
- print $q->redirect($base . '/alert');
- return '';
- }
- print $q->header( -type => 'application/xml; charset=utf-8' );
- print $out;
-}
-Page::do_fastcgi(\&main);
-
-sub rss_local_problems {
- my $q = shift;
- my $pc = $q->param('pc');
-
- my $x = $q->param('x');
- my $y = $q->param('y');
- my $lat = $q->param('lat');
- my $lon = $q->param('lon');
- my $e = $q->param('e');
- my $n = $q->param('n');
- my $d = $q->param('d') || '';
- $d = '' unless $d =~ /^\d+$/;
- my $d_str = '';
- $d_str = "/$d" if $d;
- my $state = $q->param('state') || 'all';
- $state = 'all' unless $state =~ /^(all|open|fixed)$/;
-
- # state is getting lost in the redirects. Add it on to the end as a query
- my $state_qs = '';
- $state_qs = "?state=$state" unless $state eq 'all';
-
- $state = 'confirmed' if $state eq 'open';
-
- my $qs;
- my %title_params;
- my $alert_type;
-
- my $cobrand = Page::get_cobrand($q);
- my $base = Cobrand::base_url($cobrand);
- if ($x && $y) {
- # 5000/31 as initial scale factor for these RSS feeds, now variable so redirect.
- $e = int( ($x * 5000/31) + 0.5 );
- $n = int( ($y * 5000/31) + 0.5 );
- ($lat, $lon) = Utils::convert_en_to_latlon_truncated($e, $n);
- print $q->redirect(-location => "$base/rss/l/$lat,$lon$d_str$state_qs");
- return '';
- } elsif ($e && $n) {
- ($lat, $lon) = Utils::convert_en_to_latlon_truncated($e, $n);
- print $q->redirect(-location => "$base/rss/l/$lat,$lon$d_str$state_qs");
- return '';
- } elsif ($pc) {
- my $error;
- try {
- ($lat, $lon, $error) = FixMyStreet::Geocode::lookup($pc, $q);
- } catch Error::Simple with {
- $error = shift;
- };
- if ($error) {
- print $q->redirect(-location => "$base/alert");
- return '';
- } else {
- ( $lat, $lon ) = map { Utils::truncate_coordinate($_) } ( $lat, $lon );
-
- my $pretty_pc = $pc;
- if (mySociety::PostcodeUtil::is_valid_postcode($pc)) {
- $pretty_pc = mySociety::PostcodeUtil::canonicalise_postcode($pc);
- }
- my $pretty_pc_escaped = URI::Escape::uri_escape_utf8($pretty_pc);
- $pretty_pc_escaped =~ s/%20/+/g;
- $qs = "?pc=$pretty_pc_escaped";
-
- $title_params{'POSTCODE'} = $pretty_pc;
- }
- # pass through rather than redirecting.
- } elsif ( $lat || $lon ) {
- # pass through
- } else {
- die "Missing E/N, x/y, lat/lon, or postcode parameter in RSS feed";
- }
-
- # truncate the lat,lon for nicer urls
- ( $lat, $lon ) = map { Utils::truncate_coordinate($_) } ( $lat, $lon );
-
- if (!$qs) {
- $qs = "?lat=$lat;lon=$lon";
- }
-
- if ($d) {
- $qs .= ";d=$d";
- $d = 100 if $d > 100;
- } else {
- $d = mySociety::Gaze::get_radius_containing_population($lat, $lon, 200000);
- $d = int($d*10+0.5)/10;
- mySociety::Locale::in_gb_locale {
- $d = sprintf("%f", $d);
- }
- }
-
- my $xsl = Cobrand::feed_xsl($cobrand);
-
- if ($pc) {
- $alert_type = 'postcode_local_problems';
- } else {
- $alert_type = 'local_problems';
- }
-
- my @db_params = ($lat, $lon, $d);
-
- if ($state ne 'all') {
- $alert_type .= '_state';
- push @db_params, $state;
- }
-
- return FixMyStreet::Alert::generate_rss($alert_type, $xsl, $qs, \@db_params, \%title_params, $cobrand, $q);
-}
-
diff --git a/web/test.cgi b/web/test.cgi
deleted file mode 100755
index c9b36e6b7..000000000
--- a/web/test.cgi
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# test.cgi
-# Part of test suite to force an error to check error handling works.
-#
-# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: test.cgi,v 1.1 2009-07-15 20:51:21 matthew Exp $
-
-use strict;
-use Standard;
-
-sub main {
- my $q = shift;
-
- print $q->header(-charset => 'utf-8', -content_type => 'text/plain');
- if ($q->param('error')) {
- print 10 / 0; # Cause an error by dividing by zero.
- }
- print "Success";
-}
-
-Page::do_fastcgi(\&main);
-
diff --git a/web/tms-signup.cgi b/web/tms-signup.cgi
deleted file mode 100755
index 5975a324f..000000000
--- a/web/tms-signup.cgi
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/usr/bin/perl -w -I../perllib
-
-# tms-signup.cgi
-# Showing interest in TextMyStreet
-#
-# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: tms-signup.cgi,v 1.5 2009-11-11 14:23:05 louise Exp $
-
-use strict;
-use Standard;
-use Digest::SHA1 qw(sha1_hex);
-use CrossSell;
-use mySociety::AuthToken;
-use mySociety::Config;
-use mySociety::EmailUtil qw(is_valid_email);
-use mySociety::PostcodeUtil qw(is_valid_postcode);
-use mySociety::Web qw(ent);
-
- #dbh()->'insert into textmystreet (name, email, postcode, mobile) values ()';
-
-sub main {
- my $q = shift;
- my $out = '';
- my $title = 'Confirmation';
- if (my $token = $q->param('token')) {
- my $data = mySociety::AuthToken::retrieve('tms', $token);
- if ($data->{email}) {
- $out = tms_token($q, $data);
- } else {
- $out = $q->p(<<EOF);
-Thank you for trying to confirm your interest. We seem to have a problem ourselves
-though, so <a href="/contact">please let us know what went on</a> and we'll look into it.
-EOF
- }
- } elsif ($q->param('email')) {
- $out = tms_do_subscribe($q);
- } else {
- $out = tms_updates_form($q);
- }
-
- print Page::header($q, title => $title);
- print $out;
- print Page::footer($q);
-}
-Page::do_fastcgi(\&main);
-
-sub tms_updates_form {
- my ($q, @errors) = @_;
- my @vars = qw(email name postcode mobile signed_email);
- my %input = map { $_ => $q->param($_) || '' } @vars;
- my $out = '';
- if (@errors) {
- $out .= '<ul class="error"><li>' . join('</li><li>', @errors) . '</li></ul>';
- }
- my $cobrand = Page::get_cobrand($q);
- my $display_advert = Cobrand::allow_crosssell_adverts($cobrand);
- if ($display_advert) {
- $out .= CrossSell::display_tms_form(%input);
- }
- return $out;
-}
-
-sub tms_token {
- my ($q, $data) = @_;
- my $type = $data->{type};
- my $out = '';
- if ($type eq 'subscribe') {
- tms_confirm(%$data);
- $out = $q->p('You have successfully registered your interest.');
- $out .= CrossSell::display_advert($q, $data->{email}, $data->{name}, done_tms => 1);
- }
- return $out;
-}
-
-sub tms_do_subscribe {
- my ($q) = @_;
- my @vars = qw(email name postcode mobile signed_email);
- my %input = map { $_ => $q->param($_) || '' } @vars;
-
- my @errors;
- push @errors, 'Please enter your name' unless $input{name};
- push @errors, 'Please enter a valid email address' unless is_valid_email($input{email});
- push @errors, 'Please enter a valid postcode' unless is_valid_postcode($input{postcode});
- push @errors, 'Please enter a mobile number' unless $input{mobile};
- if (@errors) {
- return tms_updates_form($q, @errors);
- }
-
- # See if email address has been signed
- if ($input{signed_email}) {
- my $out;
- if (mySociety::AuthToken::verify_with_shared_secret($input{email}, mySociety::Config::get('AUTH_SHARED_SECRET'), $input{signed_email})) {
- tms_confirm(%input);
- $out = $q->p('You have successfully registered your interest.');
- return $out;
- }
- }
-
- my %h = ();
- $h{url} = mySociety::Config::get('BASE_URL') . '/T/'
- . mySociety::AuthToken::store('tms', {
- type => 'subscribe',
- name => $input{name},
- email => $input{email},
- postcode => $input{postcode},
- mobile => $input{mobile},
- });
- dbh()->commit();
- return Page::send_confirmation_email($q, $input{email}, $input{name}, 'tms', %h);
-}
-
-sub tms_confirm {
- my %input = @_;
- dbh()->do("insert into textmystreet (name, email, postcode, mobile) values (?, ?, ?, ?)", {},
- $input{name}, $input{email}, $input{postcode}, $input{mobile});
- dbh()->commit();
-}
-
diff --git a/web/upload.cgi b/web/upload.cgi
deleted file mode 100755
index aa3f8ce61..000000000
--- a/web/upload.cgi
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/perl -w -I../perllib -I../commonlib/perllib
-
-# upload.cgi:
-# Receiver of flash upload files
-#
-# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved.
-# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: upload.cgi,v 1.2 2008-10-09 14:20:54 matthew Exp $
-
-use strict;
-use Standard -db;
-
-use Error qw(:try);
-use Image::Magick;
-use mySociety::Random qw(random_bytes);
-
-# Main code for index.cgi
-sub main {
- my $q = shift;
-
- print $q->header(-type => 'text/plain');
- my $out = ' ';
- try {
- my $fh = $q->upload('Filedata');
- my $image;
- if ($fh) {
- $q->delete('photo'); # Can't check content/type when uploaded with Flash
- $image = process_photo($fh);
- my $name = unpack('H*', random_bytes(12, 1));
- open FP, '>/data/vhost/matthew.bci.mysociety.org/photos/' . $name or throw Error::Simple('could not open file');
- print FP $image;
- close FP;
- $out = $name;
- };
- } catch Error::Simple with {
- my $e = shift;
- };
- print $out;
-}
-Page::do_fastcgi(\&main);
-
-sub process_photo {
- my $fh = shift;
- my $photo = Image::Magick->new;
- my $err = $photo->Read(file => \*$fh); # Mustn't be stringified
- close $fh;
- throw Error::Simple("read failed: $err") if "$err";
- $err = $photo->Scale(geometry => "250x250>");
- throw Error::Simple("resize failed: $err") if "$err";
- my @blobs = $photo->ImageToBlob();
- undef $photo;
- $photo = $blobs[0];
- return $photo;
-}
-
diff --git a/web/xsl.eha.xsl b/web/xsl.eha.xsl
index 14015cfd9..41b86ea07 100644
--- a/web/xsl.eha.xsl
+++ b/web/xsl.eha.xsl
@@ -8,7 +8,7 @@
<head>
<title><xsl:value-of select="$title"/> RSS Feed</title>
<link rel="stylesheet" href="/css/core.css"/>
- <link rel="stylesheet" href="/css/cobrands/emptyhomes/emptyhomes.css"/>
+ <link rel="stylesheet" href="/cobrands/emptyhomes/css.css"/>
</head>
<body>
<div id="header"><a href="http://www.emptyhomes.com/"><img border="0" src="/i/eha.png" alt="Empty Homes Agency" width="272" height="71"/></a></div>
diff --git a/web/xsl.xsl b/web/xsl.xsl
index 1aa0eef4b..12a4a93d5 100644
--- a/web/xsl.xsl
+++ b/web/xsl.xsl
@@ -18,7 +18,7 @@
<h2 class="v">Navigation</h2>
<ul id="navigation">
-<li><a href="/">Report a problem</a></li>
+<li><a href="/report/new">Report a problem</a></li>
<li><a href="/reports">All reports</a></li>
<li><a href="/faq">Help</a></li>
<li><a href="/contact">Contact</a></li>