diff options
55 files changed, 774 insertions, 433 deletions
@@ -45,6 +45,22 @@ web-based cross-browser testing tools for this project. ## Releases +* v2.0.1 (16th December 2016) + - Bugfixes: + - Fix issue in dragging map in Chrome 55. openlayers/ol2#1510 + - Don't double-decode strftime output, to fix date/time display. + - Filter category should always carry through to form. + - Don't fix height of admin multiple selects. #1589 + - Admin improvements: + - Add duplicate management to inspector view. #1581 + - Open inspect Navigate link in new tab. #1583 + - Scroll to report inspect form if present. #1583 + - Update problem lastupdate column on inspect save. #1584 + - Update priorities in inspect form on category change. #1590 + - Development improvements: + - Pass test if NXDOMAINs are intercepted. + - Better path for showing config git version. #1586 + * v2.0 (15th November 2016) - Front end improvements: - Add HTML emails. #1281 #1103 diff --git a/bin/site-specific-install.sh b/bin/site-specific-install.sh index 6c54b51d0..007cd1839 100644 --- a/bin/site-specific-install.sh +++ b/bin/site-specific-install.sh @@ -1,7 +1,7 @@ #!/bin/sh # Set this to the version we want to check out -VERSION=${VERSION_OVERRIDE:-v2.0} +VERSION=${VERSION_OVERRIDE:-v2.0.1} PARENT_SCRIPT_URL=https://github.com/mysociety/commonlib/blob/master/bin/install-site.sh diff --git a/locale/de_CH.UTF-8/LC_MESSAGES/FixMyStreet.po b/locale/de_CH.UTF-8/LC_MESSAGES/FixMyStreet.po index a854ec69b..7fea013c9 100644 --- a/locale/de_CH.UTF-8/LC_MESSAGES/FixMyStreet.po +++ b/locale/de_CH.UTF-8/LC_MESSAGES/FixMyStreet.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: fixmystreet\n" "Report-Msgid-Bugs-To: matthew@mysociety.org\n" "POT-Creation-Date: 2016-11-08 11:39+0000\n" -"PO-Revision-Date: 2016-10-25 15:13+0000\n" +"PO-Revision-Date: 2016-11-24 14:57+0100\n" "Last-Translator: mySociety <transifex@mysociety.org>\n" "Language-Team: German (Switzerland) (http://www.transifex.com/mysociety/fixmystreet/language/de_CH/)\n" "Language: de_CH\n" @@ -18,6 +18,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 1.8.11\n" #: perllib/FixMyStreet/DB/Result/Problem.pm:639 #: perllib/FixMyStreet/Script/Reports.pm:199 @@ -62,7 +63,7 @@ msgstr "" #: templates/web/base/status/stats.html:27 #: templates/web/zurich/admin/index.html:6 msgid "%s council contacts – %s confirmed, %s unconfirmed" -msgstr "" +msgstr "%s interne Stellen – %s bestätigt, %s unbestätigt" #. ("%s is the site name") #: templates/web/base/alert/index.html:7 @@ -180,7 +181,6 @@ msgid "-- Pick a property type --" msgstr "" #: templates/web/base/admin/response_templates_select.html:4 -#, fuzzy msgid "--Choose a template--" msgstr "Vorlage wählen" @@ -276,7 +276,6 @@ msgid "Add user" msgstr "User hinzufügen" #: perllib/FixMyStreet/Cobrand/Default.pm:730 -#, fuzzy msgid "Add/edit problem categories" msgstr "Füge neue Kategorie hinzu" @@ -285,7 +284,6 @@ msgid "Add/edit response priorities" msgstr "" #: perllib/FixMyStreet/Cobrand/Default.pm:731 -#, fuzzy msgid "Add/edit response templates" msgstr "Vorlagen für %s" @@ -323,15 +321,13 @@ msgid "All Reports as CSV" msgstr "Als CSV exportieren" #: templates/web/base/admin/responsepriorities/list.html:19 -#, fuzzy msgid "All categories" -msgstr "Alle Meldungen" +msgstr "Alle Kategorien" #: templates/web/base/footer.html:29 #: templates/web/base/reports/_list-filters.html:2 #: templates/web/zurich/admin/index-dm.html:12 -#: templates/web/zurich/admin/stats.html:13 -#: templates/web/zurich/footer.html:21 +#: templates/web/zurich/admin/stats.html:13 templates/web/zurich/footer.html:21 #: templates/web/zurich/nav_over_content.html:6 msgid "All reports" msgstr "Alle Meldungen" @@ -357,7 +353,6 @@ msgstr "Anonym" #: templates/web/base/report/new/form_user_loggedin.html:20 #: templates/web/base/report/update/form_name.html:13 -#, fuzzy msgid "Another user" msgstr "User hinzufügen" @@ -407,9 +402,8 @@ msgid "Assign to subdivision:" msgstr "An Fachbereich zuweisen" #: perllib/FixMyStreet/Cobrand/Default.pm:727 -#, fuzzy msgid "Assign users to areas" -msgstr "Besten Dank für Ihre Meldung. Wir haben Ihr Anliegen an %s weitergeleitet, da es nicht in den Zuständigkeitsbereich der am Pilot beteiligten Fachbereiche fällt.<br/>Freundliche Grüsse <br/>Ihre Stadt Zürich" +msgstr "User an Gegenden zuweisen" #: perllib/FixMyStreet/Cobrand/Zurich.pm:214 msgid "Assigned to %s" @@ -429,7 +423,6 @@ msgid "Authorised staff users can be associated with the categories in which the msgstr "" #: templates/web/base/admin/template_edit.html:22 -#, fuzzy msgid "Auto-response:" msgstr "Rückmeldung an User" @@ -440,7 +433,7 @@ msgstr "" #: templates/web/base/report/new/after_photo.html:6 msgid "Avoid personal information and vehicle number plates" -msgstr "" +msgstr "Verhindern Sie persönliche Informationen wie Kfz-Kennzeichen oder Personen" #: perllib/FixMyStreet/DB/Result/Problem.pm:401 #: templates/web/zurich/report/_item.html:9 @@ -452,7 +445,6 @@ msgid "Back" msgstr "Zurück" #: templates/web/base/report/_main.html:6 -#, fuzzy msgid "Back to all reports" msgstr "Alle Meldungen" @@ -498,12 +490,10 @@ msgstr "" #: perllib/FixMyStreet/Cobrand/Default.pm:650 #: templates/web/base/admin/responsepriorities/list.html:8 -#, fuzzy msgid "Categories" msgstr "Kategorie" #: templates/web/base/admin/category-checkboxes.html:2 -#, fuzzy msgid "Categories:" msgstr "Kategorie:" @@ -522,9 +512,8 @@ msgid "Category" msgstr "Kategorie" #: perllib/FixMyStreet/App/Controller/Admin.pm:854 -#, fuzzy msgid "Category changed from ‘%s’ to ‘%s’" -msgstr "z.B. ‘%s’ oder ‘%s’" +msgstr "Kategorie von ‘%s’ nach ‘%s’ geändert" #: templates/web/base/admin/stats.html:58 #: templates/web/base/admin/stats_fix_rate.html:1 @@ -550,9 +539,8 @@ msgstr "" #: templates/web/base/auth/change_email.html:1 #: templates/web/base/auth/change_email.html:3 #: templates/web/base/auth/change_email.html:30 -#, fuzzy msgid "Change email address" -msgstr "Ihre E-Mail Adresse" +msgstr "E-Mail Adresse ändern" #: templates/web/base/auth/change_password.html:1 #: templates/web/base/auth/change_password.html:16 @@ -772,9 +760,8 @@ msgid "Create category" msgstr "Kategorie erstellen" #: templates/web/base/admin/responsepriorities/edit.html:36 -#, fuzzy msgid "Create priority" -msgstr "Erfasse eine Meldung" +msgstr "Priorität erstellen" #: perllib/FixMyStreet/Cobrand/Default.pm:715 msgid "Create reports/updates as the council" @@ -846,7 +833,6 @@ msgid "Description" msgstr "Beschreibung" #: templates/web/base/admin/responsepriorities/edit.html:17 -#, fuzzy msgid "Description:" msgstr "Beschreibung" @@ -935,28 +921,24 @@ msgid "Edit body details" msgstr "Details editieren" #: perllib/FixMyStreet/Cobrand/Default.pm:724 -#, fuzzy msgid "Edit other users' details" -msgstr "Details editieren" +msgstr "User-Details editieren" #: perllib/FixMyStreet/Cobrand/Default.pm:725 msgid "Edit other users' permissions" msgstr "" #: perllib/FixMyStreet/Cobrand/Default.pm:709 -#, fuzzy msgid "Edit report category" -msgstr "Kategorie erstellen" +msgstr "Kategorie editieren" #: perllib/FixMyStreet/Cobrand/Default.pm:710 -#, fuzzy msgid "Edit report priority" -msgstr "Alle Meldungen" +msgstr "Priorität editieren" #: perllib/FixMyStreet/Cobrand/Default.pm:708 -#, fuzzy msgid "Edit reports" -msgstr "Beantwortete Meldungen" +msgstr "Meldungen editieren" #: templates/web/base/admin/report_edit.html:0 #: templates/web/base/admin/report_edit.html:14 @@ -1000,7 +982,6 @@ msgstr "" #: templates/web/base/admin/contact-form.html:36 #: templates/web/base/report/new/form_user_loggedin.html:28 #: templates/web/base/report/update/form_name.html:19 -#, fuzzy msgid "Email address" msgstr "Ihre E-Mail Adresse" @@ -1107,9 +1088,8 @@ msgstr "Beispiele:" #: templates/web/base/admin/report_edit.html:128 #: templates/web/base/report/_inspect.html:37 -#, fuzzy msgid "Existing category" -msgstr "Kategorie erstellen" +msgstr "Bestehende Kategorie" #: templates/web/base/report/new/form_report.html:52 msgid "Explain what’s wrong" @@ -1131,14 +1111,12 @@ msgid "External URL" msgstr "" #: templates/web/base/admin/report_edit.html:99 -#, fuzzy msgid "External body" -msgstr "Nachricht an zuständige Stelle" +msgstr "Externe Stelle" #: templates/web/base/admin/report_edit.html:101 -#, fuzzy msgid "External team" -msgstr "Interne Notizen" +msgstr "Externes Team" #: templates/web/base/admin/category_edit.html:25 #: templates/web/base/admin/report_edit.html:94 @@ -1146,9 +1124,8 @@ msgid "Extra data:" msgstr "" #: templates/web/base/report/_inspect.html:107 -#, fuzzy msgid "Extra details" -msgstr "Details editieren" +msgstr "Zusätzliche Details" #: templates/web/base/contact/submit.html:13 msgid "Failed to send message" @@ -1235,7 +1212,7 @@ msgstr "" #: templates/web/base/report/new/after_photo.html:3 msgid "For best results include a close-up and a wide shot" -msgstr "" +msgstr "Machen Sie ein Detail- und ein Übersichtsfoto" #: templates/web/base/admin/body-form.html:71 msgid "For more information, see <a href='http://fixmystreet.org/customising/fms_and_mapit' class='admin-offsite-link'>How FixMyStreet uses Mapit</a>." @@ -1327,7 +1304,7 @@ msgstr "Hilfe" #: templates/web/base/report/new/category_extras.html:13 #: templates/web/base/report/new/category_extras.html:14 msgid "Help <strong>%s</strong> resolve your problem quicker, by providing some extra detail. This extra information will not be published online." -msgstr "" +msgstr "Helfen Sie <strong>%s</strong> Ihre Meldung zu bearbeiten indem Sie zusätzliche Informationen angeben." #: templates/web/base/alert/_list.html:9 msgid "Here are the types of local problem alerts for ‘%s’." @@ -1512,9 +1489,8 @@ msgid "Incorrect has_photo value \"%s\"" msgstr "" #: templates/web/base/admin/contact-form.html:84 -#, fuzzy msgid "Inspection required" -msgstr "Foto benötigt" +msgstr "Kontrolle notwendig" #: perllib/FixMyStreet/Cobrand/Default.pm:712 msgid "Instruct contractors to fix problems" @@ -1597,9 +1573,8 @@ msgid "Last update:" msgstr "Letzte Bearbeitung" #: templates/web/base/reports/_list-filters.html:38 -#, fuzzy msgid "Least recently updated" -msgstr "Letzte Bearbeitung" +msgstr "Älteste Bearbeitung" #: templates/web/base/admin/body-form.html:127 msgid "Leave this blank if all reports to this body should be sent using the same send method (e.g., \"%s\")." @@ -1614,10 +1589,9 @@ msgid "Loading..." msgstr "" #: templates/web/base/alert/choose.html:1 -#: templates/web/base/alert/choose.html:3 -#: templates/web/base/alert/index.html:1 templates/web/base/alert/index.html:3 -#: templates/web/base/alert/list.html:1 templates/web/base/alert/list.html:5 -#: templates/web/base/alert/updates.html:1 +#: templates/web/base/alert/choose.html:3 templates/web/base/alert/index.html:1 +#: templates/web/base/alert/index.html:3 templates/web/base/alert/list.html:1 +#: templates/web/base/alert/list.html:5 templates/web/base/alert/updates.html:1 #: templates/web/base/tokens/confirm_alert.html:1 msgid "Local RSS feeds and email alerts" msgstr "" @@ -1692,14 +1666,12 @@ msgid "Moderate" msgstr "" #: perllib/FixMyStreet/Cobrand/Default.pm:707 -#, fuzzy msgid "Moderate report details" -msgstr "Persönliche Angaben des Meldenden mitsenden" +msgstr "Meldungsdetails anpassen" #: templates/web/base/report/_main.html:126 -#, fuzzy msgid "Moderate this report" -msgstr "Erfasse eine Meldung" +msgstr "Meldung anpassen" #: templates/web/zurich/admin/stats.html:34 msgid "Moderated by division within one working day" @@ -1711,7 +1683,7 @@ msgstr "Monat" #: templates/web/base/reports/_list-filters.html:39 msgid "Most commented" -msgstr "" +msgstr "Am meisten kommentiert" #: templates/web/base/admin/bodies.html:25 #: templates/web/base/admin/body-form.html:24 @@ -1746,9 +1718,8 @@ msgid "Name: %s" msgstr "Name: %s" #: templates/web/base/report/_inspect.html:23 -#, fuzzy msgid "Navigate to this problem" -msgstr "Änderungen an dieser Meldung, %s" +msgstr "Zu dieser Meldung zoomen" #: perllib/FixMyStreet/Geocode/FixaMinGata.pm:160 #: perllib/FixMyStreet/Geocode/OSM.pm:145 @@ -1787,9 +1758,8 @@ msgid "New category contact added" msgstr "" #: templates/web/base/auth/change_email.html:26 -#, fuzzy msgid "New email address:" -msgstr "Ihre E-Mail Adresse" +msgstr "Neue E-Mail Adresse" #: templates/web/zurich/admin/report_edit-sdm.html:109 #: templates/web/zurich/admin/report_edit.html:137 @@ -1805,15 +1775,13 @@ msgid "New note to DM:" msgstr "Neue Nachricht an die Kommunikation" #: templates/web/base/auth/change_password.html:35 -#, fuzzy msgid "New password:" -msgstr "Passwort ändern" +msgstr "Neues Passwort" #: templates/web/base/admin/responsepriorities/edit.html:4 #: templates/web/base/admin/responsepriorities/list.html:32 -#, fuzzy msgid "New priority" -msgstr "Neu" +msgstr "Neue Priorität" #: db/alert_types.pl:38 msgid "New problems for {{COUNCIL}} within {{WARD}} ward on FixMyStreet" @@ -1851,7 +1819,7 @@ msgstr "Neue Vorlage" #: templates/web/base/reports/_list-filters.html:35 msgid "Newest" -msgstr "" +msgstr "Neueste" #: templates/web/base/pagination.html:10 msgid "Next" @@ -2025,7 +1993,7 @@ msgstr "" #: templates/web/base/reports/_list-filters.html:36 msgid "Oldest" -msgstr "" +msgstr "Älteste" #: perllib/FixMyStreet/Cobrand/Zurich.pm:169 #: perllib/FixMyStreet/Cobrand/Zurich.pm:900 @@ -2058,9 +2026,8 @@ msgid "Open311 API for the mySociety FixMyStreet server" msgstr "" #: templates/web/base/admin/open311-form-fields.html:34 -#, fuzzy msgid "Open311 Jurisdiction" -msgstr "Zuständigkeit unbekannt" +msgstr "" #: templates/web/base/open311/index.html:72 msgid "Open311 initiative web page" @@ -2176,9 +2143,8 @@ msgstr "Foto benötigt" #: templates/web/base/report/new/form_report.html:28 #: templates/web/base/report/update/form_update.html:7 #: templates/web/zurich/report/new/fill_in_details_form.html:20 -#, fuzzy msgid "Photos" -msgstr "Foto" +msgstr "Fotos" #: templates/web/base/alert/list.html:23 msgid "Photos of recent nearby reports" @@ -2232,8 +2198,7 @@ msgid "Please choose a property type" msgstr "" #: perllib/FixMyStreet/App/Controller/Admin.pm:396 -#: templates/web/base/admin/bodies.html:4 -#: templates/web/base/admin/body.html:11 +#: templates/web/base/admin/bodies.html:4 templates/web/base/admin/body.html:11 msgid "Please correct the errors below" msgstr "" @@ -2260,7 +2225,6 @@ msgid "Please enter a name" msgstr "" #: perllib/FixMyStreet/App/Controller/Admin.pm:478 -#, fuzzy msgid "Please enter a name for this body" msgstr "Obligatorisches Feld" @@ -2566,9 +2530,8 @@ msgid "Provide an update" msgstr "Meldung aktualisieren" #: templates/web/base/report/update/form_name.html:9 -#, fuzzy msgid "Provide update as" -msgstr "Meldung aktualisieren" +msgstr "Meldung aktualisieren als" #: templates/web/base/auth/general.html:112 msgid "Providing a name and password is optional, but doing so will allow you to more easily report problems, leave updates and manage your reports." @@ -2585,7 +2548,6 @@ msgid "Public response:" msgstr "Rückmeldung an User" #: templates/web/base/report/_inspect.html:125 -#, fuzzy msgid "Public update:" msgstr "Rückmeldung an User" @@ -2689,9 +2651,8 @@ msgid "Recently reported problems" msgstr "Kürzlich erfasste Meldungen:" #: templates/web/base/reports/_list-filters.html:37 -#, fuzzy msgid "Recently updated" -msgstr "Kürzlich <br>beantwortet" +msgstr "Neueste Bearbeitung" #: templates/web/zurich/report/new/notes.html:5 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." @@ -2726,9 +2687,8 @@ msgid "Report" msgstr "Meldung" #: templates/web/base/report/_inspect.html:11 -#, fuzzy msgid "Report ID:" -msgstr "Meldung" +msgstr "Meldungs-ID" #: templates/web/base/footer.html:23 templates/web/base/header_logo.html:2 #: templates/web/zurich/footer.html:19 @@ -2741,9 +2701,8 @@ msgid "Report abuse" msgstr "Missbrauch melden" #: templates/web/base/report/new/form_user_loggedin.html:16 -#, fuzzy msgid "Report as" -msgstr "Meldungen" +msgstr "Melden als" #: perllib/FixMyStreet/App/Controller/Rss.pm:298 msgid "Report on %s" @@ -2853,9 +2812,8 @@ msgid "Reports published" msgstr "Beantwortet" #: templates/web/base/admin/index.html:50 -#, fuzzy msgid "Reports waiting to be sent" -msgstr "Rückmeldung ausstehend" +msgstr "Ausstehender Meldungsversand" #: templates/web/base/admin/contact-form.html:89 msgid "Reports will automatically be sent without needing to be inspected if the user's <strong>reputation</strong> is at or above this value. Set to <strong>0</strong> if all reports must be inspected regardless." @@ -2963,8 +2921,7 @@ msgstr "" msgid "Search Reports" msgstr "Meldungen suchen" -#: templates/web/base/admin/index.html:29 -#: templates/web/base/admin/users.html:1 +#: templates/web/base/admin/index.html:29 templates/web/base/admin/users.html:1 msgid "Search Users" msgstr "User suchen" @@ -3010,9 +2967,8 @@ msgid "Select which type of alert you’d like and click the button for an RSS f msgstr "" #: templates/web/base/admin/category-checkboxes.html:6 -#, fuzzy msgid "Select:" -msgstr "Titel" +msgstr "Auswählen" #: templates/web/base/admin/open311-form-fields.html:109 msgid "Send extended Open311 statuses with service request updates" @@ -3156,7 +3112,7 @@ msgstr "" #: templates/web/base/reports/_list-filters.html:33 msgid "Sort by" -msgstr "" +msgstr "Sortieren nach" #: templates/web/base/admin/user-form.html:47 msgid "Staff users have permission to log in to the admin." @@ -3191,8 +3147,7 @@ msgstr "Status" #: perllib/FixMyStreet/Cobrand/Default.pm:640 #: perllib/FixMyStreet/Cobrand/Zurich.pm:386 -#: templates/web/base/admin/index.html:65 -#: templates/web/base/admin/stats.html:1 +#: templates/web/base/admin/index.html:65 templates/web/base/admin/stats.html:1 #: templates/web/base/admin/stats_by_state.html:1 #: templates/web/zurich/admin/stats.html:1 templates/web/zurich/header.html:65 msgid "Stats" @@ -3278,15 +3233,13 @@ msgid "Summarise the problem" msgstr "Beschreiben Sie den Schaden" #: templates/web/base/admin/contact-form.html:129 -#, fuzzy msgid "Summarise your changes" -msgstr "Speichern" +msgstr "Fassen Sie Ihre Änderungen zusammen" #: perllib/FixMyStreet/Cobrand/Default.pm:638 #: perllib/FixMyStreet/Cobrand/Zurich.pm:382 #: templates/web/base/admin/bodies.html:29 -#: templates/web/base/admin/index.html:1 -#: templates/web/base/status/index.html:1 +#: templates/web/base/admin/index.html:1 templates/web/base/status/index.html:1 #: templates/web/base/status/index.html:3 #: templates/web/zurich/admin/index-dm.html:1 #: templates/web/zurich/admin/index-sdm.html:1 @@ -3521,7 +3474,7 @@ msgstr "" msgid "" "The user's <strong>name</strong> is displayed publicly on reports that have not been marked <em>anonymous</em>.\n" " Names are not necessarily unique." -msgstr "" +msgstr "Namen müssen nicht eindeutig sein." #: templates/web/base/around/on_map_list_items.html:12 #: templates/web/base/my/_problem-list.html:8 @@ -3736,9 +3689,8 @@ msgid "Trusted to make reports that don't need to be inspected" msgstr "" #: templates/web/base/admin/user-form.html:119 -#, fuzzy msgid "Trusted:" -msgstr "Erstellt" +msgstr "Vertrauenswürdig" #: templates/web/base/js/translation_strings.html:29 msgid "Try again" @@ -3897,13 +3849,12 @@ msgstr "" #: templates/web/base/admin/users.html:5 msgid "User search finds matches in users' names and email addresses." -msgstr "" +msgstr "Diese Suche sucht in den Namen der Benützer und deren E-Mail-Adressen." #: perllib/FixMyStreet/Cobrand/Default.pm:670 #: perllib/FixMyStreet/Cobrand/Default.pm:723 #: perllib/FixMyStreet/Cobrand/Zurich.pm:398 -#: templates/web/base/admin/flagged.html:29 -#: templates/web/zurich/header.html:61 +#: templates/web/base/admin/flagged.html:29 templates/web/zurich/header.html:61 msgid "Users" msgstr "" @@ -4160,9 +4111,8 @@ msgstr "Ihre Meldungen" #: templates/web/base/auth/change_password.html:11 #: templates/web/base/footer.html:26 templates/web/base/my/my.html:18 -#, fuzzy msgid "Your account" -msgstr "Account bestätigen" +msgstr "Ihr Account" #: templates/web/base/alert/_list.html:90 #: templates/web/base/alert/updates.html:19 @@ -4223,9 +4173,8 @@ msgstr "Ihre Meldungen" #: templates/web/base/my/planned.html:0 templates/web/base/my/planned.html:18 #: templates/web/base/my/planned.html:5 -#, fuzzy msgid "Your shortlist" -msgstr "Ihre Meldungen" +msgstr "Ihre Favoriten" #: templates/web/base/my/my.html:51 msgid "Your updates" @@ -4233,9 +4182,8 @@ msgstr "" #: templates/web/base/report/new/form_user_loggedin.html:18 #: templates/web/base/report/update/form_name.html:11 -#, fuzzy msgid "Yourself" -msgstr "E-Mail Adresse" +msgstr "Sie selbst" #: templates/web/base/admin/category-checkboxes.html:7 #: templates/web/base/admin/user-form.html:159 @@ -4332,9 +4280,8 @@ msgid "none" msgstr "" #: templates/web/base/admin/category_edit.html:35 -#, fuzzy msgid "optional" -msgstr "(optional)" +msgstr "optional" #: templates/web/base/alert/_list.html:86 msgid "or" @@ -4369,9 +4316,8 @@ msgid "reopened" msgstr "" #: templates/web/base/admin/category_edit.html:35 -#, fuzzy msgid "required" -msgstr "Foto benötigt" +msgstr "benötigt" #: templates/web/zurich/footer.html:13 msgid "sign out" diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm index e91597bb0..bbdf449aa 100644 --- a/perllib/FixMyStreet/App/Controller/Admin.pm +++ b/perllib/FixMyStreet/App/Controller/Admin.pm @@ -129,7 +129,7 @@ sub index : Path : Args(0) { sub config_page : Path( 'config' ) : Args(0) { my ($self, $c) = @_; - my $dir = $c->stash->{additional_template_paths}->[0]; + my $dir = FixMyStreet->path_to(); my $git_version = `cd $dir && git describe --tags`; chomp $git_version; $c->stash( @@ -1377,7 +1377,7 @@ sub user_edit : Path('user_edit') : Args(1) { } if ( $user->from_body ) { - unless ( $c->stash->{body} && $user->from_body->id eq $c->stash->{body}->id ) { + unless ( $c->stash->{live_contacts} ) { $c->stash->{body} = $user->from_body; $c->forward('fetch_contacts'); } diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm index 73479c584..5a1cfbe54 100644 --- a/perllib/FixMyStreet/App/Controller/Report.pm +++ b/perllib/FixMyStreet/App/Controller/Report.pm @@ -231,13 +231,8 @@ sub generate_map_tags : Private { latitude => $problem->latitude, longitude => $problem->longitude, pins => $problem->used_map - ? [ { - latitude => $problem->latitude, - longitude => $problem->longitude, - colour => $c->cobrand->pin_colour($problem, 'report'), - type => 'big', - } ] - : [], + ? [ $problem->pin_data($c, 'report', type => 'big') ] + : [], ); return 1; @@ -300,6 +295,7 @@ sub action_router : Path('') : Args(2) { my ( $self, $c, $id, $action ) = @_; $c->go( 'map', [ $id ] ) if $action eq 'map'; + $c->go( 'nearby_json', [ $id ] ) if $action eq 'nearby.json'; $c->detach( '/page_error_404_not_found', [] ); } @@ -312,15 +308,38 @@ sub inspect : Private { $c->stash->{categories} = $c->forward('/admin/categories_for_point'); $c->stash->{report_meta} = { map { $_->{name} => $_ } @{ $c->stash->{problem}->get_extra_fields() } }; + my %category_body = map { $_->category => $_->body_id } map { $_->contacts->all } values %{$problem->bodies}; + + my @priorities = $c->model('DB::ResponsePriority')->for_bodies($problem->bodies_str_ids)->all; + my $priorities_by_category = {}; + foreach my $pri (@priorities) { + my $any = 0; + foreach ($pri->contacts->all) { + $any = 1; + push @{$priorities_by_category->{$_->category}}, $pri->id . '=' . URI::Escape::uri_escape_utf8($pri->name); + } + if (!$any) { + foreach (grep { $category_body{$_} == $pri->body_id } @{$c->stash->{categories}}) { + push @{$priorities_by_category->{$_}}, $pri->id . '=' . URI::Escape::uri_escape_utf8($pri->name); + } + } + } + foreach (keys %{$priorities_by_category}) { + $priorities_by_category->{$_} = join('&', @{$priorities_by_category->{$_}}); + } + + $c->stash->{priorities_by_category} = $priorities_by_category; + if ( $c->get_param('save') ) { $c->forward('/auth/check_csrf_token'); my $valid = 1; my $update_text; my $reputation_change = 0; + my %update_params = (); if ($permissions->{report_inspect}) { - foreach (qw/detailed_information traffic_information/) { + foreach (qw/detailed_information traffic_information duplicate_of/) { $problem->set_extra_metadata( $_ => $c->get_param($_) ); } @@ -345,15 +364,19 @@ sub inspect : Private { if ( $problem->state eq 'hidden' ) { $problem->get_photoset->delete_cached; } + if ( $problem->state eq 'duplicate' && $old_state ne 'duplicate' ) { + # If the report is being closed as duplicate, make sure the + # update records this. + $update_params{problem_state} = "duplicate"; + } + if ( $problem->state ne 'duplicate' ) { + $problem->unset_extra_metadata('duplicate_of'); + } if ( $problem->state ne $old_state ) { $c->forward( '/admin/log_edit', [ $problem->id, 'problem', 'state_change' ] ); } } - if ($c->get_param('priority') && ($permissions->{report_inspect} || $permissions->{report_edit_priority})) { - $problem->response_priority( $problem->response_priorities->find({ id => $c->get_param('priority') }) ); - } - if ( !$c->forward( '/admin/report_edit_location', [ $problem ] ) ) { # New lat/lon isn't valid, show an error $valid = 0; @@ -373,10 +396,16 @@ sub inspect : Private { $c->forward('/report/new/set_report_extras', [ \@contacts, $param_prefix ]); } + # Updating priority must come after category, in case category has changed (and so might have priorities) + if ($c->get_param('priority') && ($permissions->{report_inspect} || $permissions->{report_edit_priority})) { + $problem->response_priority( $problem->response_priorities->find({ id => $c->get_param('priority') }) ); + } + if ($valid) { if ( $reputation_change != 0 ) { $problem->user->update_reputation($reputation_change); } + $problem->lastupdate( \'current_timestamp' ); $problem->update; if ( defined($update_text) ) { $problem->add_to_comments( { @@ -388,6 +417,7 @@ sub inspect : Private { state => 'confirmed', mark_fixed => 0, anonymous => 0, + %update_params, } ); } # This problem might no longer be visible on the current cobrand, @@ -416,6 +446,38 @@ sub map : Private { } +sub nearby_json : Private { + my ( $self, $c, $id ) = @_; + + $c->forward( 'load_problem_or_display_error', [ $id ] ); + my $p = $c->stash->{problem}; + my $dist = 1000; + + my $nearby = $c->model('DB::Nearby')->nearby( + $c, $dist, [ $p->id ], 5, $p->latitude, $p->longitude, undef, [ $p->category ], undef + ); + my @pins = map { + my $p = $_->problem; + my $colour = $c->cobrand->pin_colour( $p, 'around' ); + [ $p->latitude, $p->longitude, + $colour, + $p->id, $p->title_safe, 'small', JSON->false + ] + } @$nearby; + + my $on_map_list_html = $c->render_fragment( + 'around/on_map_list_items.html', + { on_map => [], around_map => $nearby } + ); + + my $json = { pins => \@pins }; + $json->{current} = $on_map_list_html if $on_map_list_html; + my $body = encode_json($json); + $c->res->content_type('application/json; charset=utf-8'); + $c->res->body($body); +} + + =head2 check_has_permission_to Ensure the currently logged-in user has any of the provided permissions applied diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm index a2ca9f8c1..27111deb2 100644 --- a/perllib/FixMyStreet/Cobrand/Default.pm +++ b/perllib/FixMyStreet/Cobrand/Default.pm @@ -1184,6 +1184,7 @@ Return true if an Open311 service attribute should be a hidden field. sub category_extra_hidden { my ($self, $meta) = @_; + return 0; } 1; diff --git a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm index a476b5e9b..dca208e98 100644 --- a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm +++ b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm @@ -119,4 +119,6 @@ sub contact_email { return join( '@', 'highway.enquiries', 'oxfordshire.gov.uk' ); } +sub admin_user_domain { 'oxfordshire.gov.uk' } + 1; diff --git a/perllib/FixMyStreet/Cobrand/UKCouncils.pm b/perllib/FixMyStreet/Cobrand/UKCouncils.pm index 42c9c5cbc..c22224307 100644 --- a/perllib/FixMyStreet/Cobrand/UKCouncils.pm +++ b/perllib/FixMyStreet/Cobrand/UKCouncils.pm @@ -55,8 +55,9 @@ sub updates_restriction { sub users_restriction { my ($self, $rs) = @_; - # Council admins can only see users who are members of the same council or - # users who have sent a report or update to that council. + # Council admins can only see users who are members of the same council, + # have an email address in a specified domain, or users who have sent a + # report or update to that council. my $problem_user_ids = $self->problems->search( undef, @@ -73,10 +74,16 @@ sub users_restriction { } )->as_query; - return $rs->search([ + my $or_query = [ from_body => $self->council_id, id => [ { -in => $problem_user_ids }, { -in => $update_user_ids } ], - ]); + ]; + if ($self->can('admin_user_domain')) { + my $domain = $self->admin_user_domain; + push @$or_query, email => { ilike => "%\@$domain" }; + } + + return $rs->search($or_query); } sub base_url { diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index f421394fa..203e72fae 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -181,6 +181,7 @@ use namespace::clean -except => [ 'meta' ]; use Utils; use FixMyStreet::Map::FMS; use LWP::Simple qw($ua); +use RABX; my $IM = eval { require Image::Magick; @@ -475,7 +476,7 @@ sub confirm { sub bodies_str_ids { my $self = shift; - return unless $self->bodies_str; + return [] unless $self->bodies_str; my @bodies = split( /,/, $self->bodies_str ); return \@bodies; } @@ -680,16 +681,7 @@ alphabetical order of name. sub response_priorities { my $self = shift; - return $self->result_source->schema->resultset('ResponsePriority')->search( - { - 'me.body_id' => $self->bodies_str_ids, - 'contact.category' => [ $self->category, undef ], - }, - { - order_by => 'name', - join => { 'contact_response_priorities' => 'contact' }, - } - ); + return $self->result_source->schema->resultset('ResponsePriority')->for_bodies($self->bodies_str_ids, $self->category); } # returns true if the external id is the council's ref, i.e., useful to publish it @@ -941,6 +933,7 @@ sub pin_data { id => $self->id, title => $opts{private} ? $self->title : $self->title_safe, problem => $self, + type => $opts{type}, } }; @@ -1032,4 +1025,27 @@ has shortlisted_user => ( }, ); +has duplicate_of => ( + is => 'ro', + lazy => 1, + default => sub { + my $self = shift; + return unless $self->state eq 'duplicate'; + my $duplicate_of = int($self->get_extra_metadata("duplicate_of") || 0); + return unless $duplicate_of; + return $self->result_source->schema->resultset('Problem')->search({ id => $duplicate_of })->first; + }, +); + +has duplicates => ( + is => 'ro', + lazy => 1, + default => sub { + my $self = shift; + my $rabx_id = RABX::serialise( $self->id ); + my @duplicates = $self->result_source->schema->resultset('Problem')->search({ extra => { like => "\%duplicate_of,$rabx_id%" } })->all; + return \@duplicates; + }, +); + 1; diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm index 028394795..f4e5144f8 100644 --- a/perllib/FixMyStreet/DB/Result/User.pm +++ b/perllib/FixMyStreet/DB/Result/User.pm @@ -268,7 +268,7 @@ sub has_permission_to { my ($self, $permission_type, $body_ids) = @_; return 1 if $self->is_superuser; - return 0 unless $body_ids; + return 0 if !$body_ids || (ref $body_ids && !@$body_ids); my $permission = $self->user_body_permissions->find({ permission_type => $permission_type, diff --git a/perllib/FixMyStreet/DB/ResultSet/ResponsePriority.pm b/perllib/FixMyStreet/DB/ResultSet/ResponsePriority.pm new file mode 100644 index 000000000..aa9c426f4 --- /dev/null +++ b/perllib/FixMyStreet/DB/ResultSet/ResponsePriority.pm @@ -0,0 +1,22 @@ +package FixMyStreet::DB::ResultSet::ResponsePriority; +use base 'DBIx::Class::ResultSet'; + +use strict; +use warnings; + +sub for_bodies { + my ($rs, $bodies, $category) = @_; + my $attrs = { + 'me.body_id' => $bodies, + }; + if ($category) { + $attrs->{'contact.category'} = [ $category, undef ]; + } + $rs->search($attrs, { + order_by => 'name', + join => { 'contact_response_priorities' => 'contact' }, + distinct => 1, + }); +} + +1; diff --git a/perllib/FixMyStreet/Map/Bing.pm b/perllib/FixMyStreet/Map/Bing.pm index 4c1887641..617823b45 100644 --- a/perllib/FixMyStreet/Map/Bing.pm +++ b/perllib/FixMyStreet/Map/Bing.pm @@ -6,8 +6,7 @@ use base 'FixMyStreet::Map::OSM'; use strict; -# Is set by the JavaScript -sub map_type { '""' } +sub map_type { '' } sub map_template { 'bing' } diff --git a/perllib/FixMyStreet/Map/Bristol.pm b/perllib/FixMyStreet/Map/Bristol.pm index 3b60d1acf..c2925ff8d 100644 --- a/perllib/FixMyStreet/Map/Bristol.pm +++ b/perllib/FixMyStreet/Map/Bristol.pm @@ -60,9 +60,7 @@ sub copyright { return '© BCC'; } -sub map_type { - return 'bristol'; -} +sub map_template { 'bristol' } # Reproject a WGS84 lat/lon into BNG easting/northing sub reproject_from_latlon($$$) { diff --git a/perllib/FixMyStreet/Map/Bromley.pm b/perllib/FixMyStreet/Map/Bromley.pm index fc8726b34..0cd36a2ac 100644 --- a/perllib/FixMyStreet/Map/Bromley.pm +++ b/perllib/FixMyStreet/Map/Bromley.pm @@ -9,9 +9,7 @@ use base 'FixMyStreet::Map::FMS'; use strict; -sub map_type { - return '[ [ "", "a-" ], "https://{S}fix.bromley.gov.uk/tilma" ]'; -} +sub map_template { 'bromley' } sub map_tile_base { '-', "https://%sfix.bromley.gov.uk/tilma/%d/%d/%d.png"; diff --git a/perllib/FixMyStreet/Map/GoogleOL.pm b/perllib/FixMyStreet/Map/GoogleOL.pm index 2dfb697e5..55032d707 100644 --- a/perllib/FixMyStreet/Map/GoogleOL.pm +++ b/perllib/FixMyStreet/Map/GoogleOL.pm @@ -9,12 +9,8 @@ use parent 'FixMyStreet::Map::OSM'; use strict; -sub map_type { - return '""'; -} +sub map_type { '' } -sub map_template { - return 'google-ol'; -} +sub map_template { 'google-ol' } 1; diff --git a/perllib/FixMyStreet/Map/OSM.pm b/perllib/FixMyStreet/Map/OSM.pm index d4000f1a4..76af99d36 100644 --- a/perllib/FixMyStreet/Map/OSM.pm +++ b/perllib/FixMyStreet/Map/OSM.pm @@ -14,13 +14,9 @@ use Utils; use constant ZOOM_LEVELS => 6; use constant MIN_ZOOM_LEVEL => 13; -sub map_type { - return 'OpenLayers.Layer.OSM.Mapnik'; -} +sub map_type { 'OpenLayers.Layer.OSM.Mapnik' } -sub map_template { - return 'osm'; -} +sub map_template { 'osm' } sub map_tiles { my ( $self, %params ) = @_; diff --git a/perllib/FixMyStreet/Map/OSM/CycleMap.pm b/perllib/FixMyStreet/Map/OSM/CycleMap.pm index 8f1de39d2..bb6aefc8c 100644 --- a/perllib/FixMyStreet/Map/OSM/CycleMap.pm +++ b/perllib/FixMyStreet/Map/OSM/CycleMap.pm @@ -9,9 +9,7 @@ use base 'FixMyStreet::Map::OSM'; use strict; -sub map_type { - return 'OpenLayers.Layer.OSM.CycleMap'; -} +sub map_type { 'OpenLayers.Layer.OSM.CycleMap' } sub base_tile_url { return 'tile.opencyclemap.org/cycle'; diff --git a/perllib/FixMyStreet/Map/OSM/MapQuest.pm b/perllib/FixMyStreet/Map/OSM/MapQuest.pm index 3b0df14b1..ac80e61b5 100644 --- a/perllib/FixMyStreet/Map/OSM/MapQuest.pm +++ b/perllib/FixMyStreet/Map/OSM/MapQuest.pm @@ -9,13 +9,9 @@ use base 'FixMyStreet::Map::OSM'; use strict; -sub map_type { - return 'OpenLayers.Layer.OSM.MapQuestOpen'; -} +sub map_type { 'OpenLayers.Layer.OSM.MapQuestOpen' } -sub map_template { - return 'mapquest-attribution'; -} +sub map_template { 'mapquest-attribution' } sub map_tiles { my ( $self, %params ) = @_; diff --git a/perllib/FixMyStreet/Map/OSM/StreetView.pm b/perllib/FixMyStreet/Map/OSM/StreetView.pm index f37deaaae..12fbdb19d 100644 --- a/perllib/FixMyStreet/Map/OSM/StreetView.pm +++ b/perllib/FixMyStreet/Map/OSM/StreetView.pm @@ -9,13 +9,9 @@ use base 'FixMyStreet::Map::OSM'; use strict; -sub map_type { - return '""'; -} +sub map_type { '' } -sub map_template { - return 'osm-streetview'; -} +sub map_template { 'osm-streetview' } sub base_tile_url { return 'os.openstreetmap.org/sv'; diff --git a/perllib/FixMyStreet/Map/WMTSBase.pm b/perllib/FixMyStreet/Map/WMTSBase.pm index 909ada1d6..960a58a41 100644 --- a/perllib/FixMyStreet/Map/WMTSBase.pm +++ b/perllib/FixMyStreet/Map/WMTSBase.pm @@ -59,9 +59,7 @@ sub tile_parameters { } # This is used to determine which template to render the map with -sub map_type { - return 'fms'; -} +sub map_template { 'fms' } # Reproject a WGS84 lat/lon into an x/y coordinate in this map's CRS. # Subclasses will want to override this. @@ -198,7 +196,7 @@ sub get_map_hash { my @scales = $self->scales; return { %params, - type => $self->map_type, + type => $self->map_template, map_type => 'OpenLayers.Layer.WMTS', tiles => $self->map_tiles( %params ), copyright => $self->copyright(), diff --git a/perllib/FixMyStreet/Map/Zurich.pm b/perllib/FixMyStreet/Map/Zurich.pm index 1e302bdda..ed68daeee 100644 --- a/perllib/FixMyStreet/Map/Zurich.pm +++ b/perllib/FixMyStreet/Map/Zurich.pm @@ -67,9 +67,7 @@ sub copyright { return '© Stadt Zürich'; } -sub map_type { - return 'zurich'; -} +sub map_template { 'zurich' } # Reproject a WGS84 lat/lon into Swiss easting/northing diff --git a/perllib/Utils.pm b/perllib/Utils.pm index 84c09d09d..7dd2a3f39 100644 --- a/perllib/Utils.pm +++ b/perllib/Utils.pm @@ -178,16 +178,17 @@ sub prettify_dt { } $tt .= ', ' unless $type eq 'date'; if ($dt->strftime('%Y %U') eq $now->strftime('%Y %U')) { - $tt .= decode_utf8($dt->strftime('%A')); + $tt .= $dt->strftime('%A'); } elsif ($type eq 'zurich') { - $tt .= decode_utf8($dt->strftime('%e. %B %Y')); + $tt .= $dt->strftime('%e. %B %Y'); } elsif ($type eq 'short') { - $tt .= decode_utf8($dt->strftime('%e %b %Y')); + $tt .= $dt->strftime('%e %b %Y'); } elsif ($dt->strftime('%Y') eq $now->strftime('%Y')) { - $tt .= decode_utf8($dt->strftime('%A %e %B %Y')); + $tt .= $dt->strftime('%A %e %B %Y'); } else { - $tt .= decode_utf8($dt->strftime('%a %e %B %Y')); + $tt .= $dt->strftime('%a %e %B %Y'); } + $tt = decode_utf8($tt) if !utf8::is_utf8($tt); return $tt; } diff --git a/t/Mock/MapItZurich.pm b/t/Mock/MapItZurich.pm index ece9a9b22..9195749f6 100644 --- a/t/Mock/MapItZurich.pm +++ b/t/Mock/MapItZurich.pm @@ -38,6 +38,12 @@ sub dispatch_request { my $json = $self->json->encode({}); return [ 200, [ 'Content-Type' => 'application/json' ], [ $json ] ]; }, + + sub (GET + /area/*/children) { + my ($self, $area) = @_; + my $json = $self->json->encode({}); + return [ 200, [ 'Content-Type' => 'application/json' ], [ $json ] ]; + }, } __PACKAGE__->run_if_script; diff --git a/t/app/controller/auth.t b/t/app/controller/auth.t index 251aa2977..22ade6f4b 100644 --- a/t/app/controller/auth.t +++ b/t/app/controller/auth.t @@ -2,6 +2,7 @@ use strict; use warnings; use Test::More; +use Test::MockModule; use FixMyStreet::TestMech; my $mech = FixMyStreet::TestMech->new; @@ -33,6 +34,13 @@ for my $test ( ) { my ( $email, $error_message ) = @$test; + + my $resolver = Test::MockModule->new('Net::DNS::Resolver'); + $resolver->mock('send', sub { + my ($self, $domain, $type) = @_; + return Net::DNS::Packet->new; + }); + pass "--- testing bad email '$email' gives error '$error_message'"; $mech->get_ok('/auth'); is_deeply $mech->page_errors, [], 'no errors initially'; diff --git a/t/app/controller/report_display.t b/t/app/controller/report_display.t index fb532ddc4..fad8b2bbb 100644 --- a/t/app/controller/report_display.t +++ b/t/app/controller/report_display.t @@ -25,31 +25,16 @@ my $dt = DateTime->new( second => 23 ); -my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( - { - postcode => 'SW1A 1AA', - bodies_str => '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 $westminster = $mech->create_body_ok(2504, 'Westminster City Council'); +my ($report, $report2) = $mech->create_problems_for_body(2, $westminster->id, "Example", { + user => $user, + confirmed => $dt->ymd . ' ' . $dt->hms, +}); +$report->update({ + title => 'Test 2', + detail => 'Test 2 Detail' +}); 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'); @@ -125,6 +110,22 @@ subtest "check owner of report can view non public reports" => sub { ok $report->update( { non_public => 0 } ), 'make report public'; }; +subtest "duplicate reports are signposted correctly" => sub { + $report2->set_extra_metadata(duplicate_of => $report->id); + $report2->state('duplicate'); + $report2->update; + + my $report2_id = $report2->id; + ok $mech->get("/report/$report2_id"), "get '/report/$report2_id'"; + $mech->content_contains('This report is a duplicate'); + $mech->content_contains($report->title); + $mech->log_out_ok; + + $report2->unset_extra_metadata('duplicate_of'); + $report2->state('confirmed'); + $report2->update; +}; + subtest "test a good report" => sub { $mech->get_ok("/report/$report_id"); is $mech->uri->path, "/report/$report_id", "at /report/$report_id"; @@ -532,5 +533,6 @@ subtest "Zurich banners are displayed correctly" => sub { END { $mech->delete_user('test@example.com'); + $mech->delete_body($westminster); done_testing(); } diff --git a/t/app/controller/report_inspect.t b/t/app/controller/report_inspect.t index efb32d116..4697cc9d1 100644 --- a/t/app/controller/report_inspect.t +++ b/t/app/controller/report_inspect.t @@ -21,12 +21,14 @@ my $wodc = $mech->create_body_ok(2420, 'West Oxfordshire District Council', id = $mech->create_contact_ok( body_id => $wodc->id, category => 'Horses', email => 'horses@example.net' ); -my ($report) = $mech->create_problems_for_body(1, $oxon->id, 'Test', { +my ($report, $report2) = $mech->create_problems_for_body(2, $oxon->id, 'Test', { category => 'Cows', cobrand => 'fixmystreet', areas => ',2237,2420', whensent => \'current_timestamp', latitude => 51.847693, longitude => -1.355908, }); my $report_id = $report->id; +my $report2_id = $report2->id; + my $user = $mech->log_in_ok('test@example.com'); $user->update( { from_body => $oxon } ); @@ -93,6 +95,59 @@ FixMyStreet::override_config { $mech->content_lacks('Invalid location'); }; + subtest "test duplicate reports are shown" => sub { + my $old_state = $report->state; + $report->set_extra_metadata('duplicate_of' => $report2->id); + $report->state('duplicate'); + $report->update; + + $mech->get_ok("/report/$report_id"); + $mech->content_contains($report2->title); + + $mech->get_ok("/report/$report2_id"); + $mech->content_contains($report->title); + + $report->unset_extra_metadata('duplicate_of'); + $report->state($old_state); + $report->update; + }; + + subtest "marking a report as a duplicate with update correctly sets update status" => sub { + my $old_state = $report->state; + $report->comments->delete_all; + + $mech->get_ok("/report/$report_id"); + $mech->submit_form_ok({ button => 'save', with_fields => { state => 'Duplicate', duplicate_of => $report2->id, public_update => "This is a duplicate.", save_inspected => "1" } }); + $report->discard_changes; + + is $report->state, 'duplicate', 'report marked as duplicate'; + is $report->comments->search({ problem_state => 'duplicate' })->count, 1, 'update marking report as duplicate was left'; + + $report->update({ state => $old_state }); + }; + + subtest "marking a report as a duplicate doesn't clobber user-provided update" => sub { + my $old_state = $report->state; + $report->comments->delete_all; + + $mech->get_ok("/report/$report_id"); + my $update_text = "This text was entered as an update by the user."; + $mech->submit_form_ok({ button => 'save', with_fields => { + state => 'Duplicate', + duplicate_of => $report2->id, + public_update => $update_text, + save_inspected => "1", + }}); + $report->discard_changes; + + is $report->state, 'duplicate', 'report marked as duplicate'; + is $report->comments->search({ problem_state => 'duplicate' })->count, 1, 'update marked report as duplicate'; + $mech->content_contains($update_text); + $mech->content_lacks("Thank you for your report. This problem has already been reported."); + + $report->update({ state => $old_state }); + }; + foreach my $test ( { type => 'report_edit_priority', priority => 1 }, { type => 'report_edit_category', category => 1 }, @@ -102,8 +157,8 @@ FixMyStreet::override_config { $user->user_body_permissions->delete; $user->user_body_permissions->create({ body => $oxon, permission_type => $test->{type} }); $mech->get_ok("/report/$report_id"); - $mech->contains_or_lacks($test->{priority}, 'Priority'); - $mech->contains_or_lacks($test->{priority}, 'High'); + $mech->contains_or_lacks($test->{priority}, 'Priority</label>'); + $mech->contains_or_lacks($test->{priority}, '>High'); $mech->contains_or_lacks($test->{category}, 'Category'); $mech->contains_or_lacks($test->{detailed}, 'Extra details'); $mech->submit_form_ok({ diff --git a/t/app/controller/report_updates.t b/t/app/controller/report_updates.t index e1cd0da71..5a88097fa 100644 --- a/t/app/controller/report_updates.t +++ b/t/app/controller/report_updates.t @@ -685,6 +685,8 @@ for my $test ( my $meta_state = $test->{meta} || $test->{fields}->{state}; if ( $test->{reopened} ) { like $update_meta->[0], qr/reopened$/, 'update meta says reopened'; + } elsif ( $test->{state} eq 'duplicate' ) { + like $update_meta->[0], qr/closed as $meta_state$/, 'update meta includes state change'; } else { like $update_meta->[0], qr/marked as $meta_state$/, 'update meta includes state change'; } diff --git a/t/app/model/problem.t b/t/app/model/problem.t index bd7d0e55c..1130078c0 100644 --- a/t/app/model/problem.t +++ b/t/app/model/problem.t @@ -763,6 +763,17 @@ subtest 'check response templates' => sub { is $problem->response_templates, 2, 'Global and pothole templates returned'; }; +subtest 'check duplicate reports' => sub { + my ($problem1, $problem2) = $mech->create_problems_for_body(2, $body_ids{2651}, 'TITLE'); + $problem1->set_extra_metadata(duplicate_of => $problem2->id); + $problem1->state('duplicate'); + $problem1->update; + + is $problem1->duplicate_of->title, $problem2->title, 'problem1 returns correct problem from duplicate_of'; + is scalar @{ $problem2->duplicates }, 1, 'problem2 has correct number of duplicates'; + is $problem2->duplicates->[0]->title, $problem1->title, 'problem2 includes problem1 in duplicates'; +}; + END { $problem->comments->delete if $problem; $problem->delete if $problem; diff --git a/t/cobrand/zurich.t b/t/cobrand/zurich.t index b59e546dd..ddaae1f90 100644 --- a/t/cobrand/zurich.t +++ b/t/cobrand/zurich.t @@ -102,8 +102,9 @@ sub get_export_rows_count { my $EXISTING_REPORT_COUNT = 0; +my $superuser; subtest "set up superuser" => sub { - my $superuser = $mech->log_in_ok( 'super@example.org' ); + $superuser = $mech->log_in_ok( 'super@example.org' ); # a user from body $zurich is a superuser, as $zurich has no parent id! $superuser->update({ from_body => $zurich->id }); $EXISTING_REPORT_COUNT = get_export_rows_count($mech); @@ -968,6 +969,18 @@ subtest 'time_spent' => sub { $mech->log_out_ok; +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'zurich' ], + MAPIT_URL => 'http://mapit.zurich/', + MAPIT_TYPES => [ 'ZZZ' ], +}, sub { + LWP::Protocol::PSGI->register(t::Mock::MapItZurich->run_if_script, host => 'mapit.zurich'); + subtest 'users at the top level can be edited' => sub { + $mech->log_in_ok( $superuser->email ); + $mech->get_ok('/admin/user_edit/' . $superuser->id ); + }; +}; + END { $mech->delete_body($subdivision); $mech->delete_body($division); diff --git a/templates/web/base/common_header_tags.html b/templates/web/base/common_header_tags.html index f34dea212..7c0ac4973 100644 --- a/templates/web/base/common_header_tags.html +++ b/templates/web/base/common_header_tags.html @@ -1,5 +1,7 @@ [% SET start = c.config.ADMIN_BASE_URL IF admin %] +<link rel="stylesheet" href="[% version('/js/OpenLayers/theme/default/style.css') %]"> + <meta http-equiv="content-type" content="text/html; charset=utf-8" id="js-meta-data" data-page="[% page %]" data-cobrand="[% c.cobrand.moniker %]"> diff --git a/templates/web/base/contact/index.html b/templates/web/base/contact/index.html index 536b95527..f24f8afea 100644 --- a/templates/web/base/contact/index.html +++ b/templates/web/base/contact/index.html @@ -40,14 +40,12 @@ <input type="hidden" name="id" value="[% update.problem_id %]"> [% ELSIF problem %] - <p> [% IF moderation_complaint %] <input type="hidden" name="m" value="[% moderation_complaint %]"> - [% loc('You are complaining that this problem report was unnecessarily moderated:') %] + <p>[% loc('You are complaining that this problem report was unnecessarily moderated:') %]</p> [% ELSE %] - [% loc('You are reporting the following problem report for being abusive, containing personal information, or similar:') %] + [% INCLUDE 'contact/unsuitable-text.html' %] [% END %] - </p> <blockquote> <h2>[% problem.title_safe | html %]</h2> diff --git a/templates/web/base/contact/unsuitable-text.html b/templates/web/base/contact/unsuitable-text.html new file mode 100644 index 000000000..12137c818 --- /dev/null +++ b/templates/web/base/contact/unsuitable-text.html @@ -0,0 +1,3 @@ +<p> + [% loc('You are reporting the following problem report for being abusive, containing personal information, or similar:') %] +</p> diff --git a/templates/web/base/maps/openlayers.html b/templates/web/base/maps/openlayers.html index bc71847ad..18829c5c4 100644 --- a/templates/web/base/maps/openlayers.html +++ b/templates/web/base/maps/openlayers.html @@ -19,7 +19,7 @@ data-pin_prefix='[% c.config.ADMIN_BASE_URL IF admin %][% c.cobrand.path_to_pin_icons %]', data-numZoomLevels=[% map.numZoomLevels %] data-zoomOffset=[% map.zoomOffset %] - data-map_type=[% map.map_type %] + data-map_type="[% map.map_type %]" [% IF include_key -%] data-key='[% c.config.BING_MAPS_API_KEY %]' [%- END %] diff --git a/templates/web/base/report/_inspect.html b/templates/web/base/report/_inspect.html index b85fec0bb..06c3aab6c 100644 --- a/templates/web/base/report/_inspect.html +++ b/templates/web/base/report/_inspect.html @@ -9,7 +9,7 @@ <div class="inspect-section"> <p> <strong>[% loc('Report ID:') %]</strong> - [% problem.id %] + <span class="js-report-id">[% problem.id %]</span> </p> <p> [% SET local_coords = problem.local_coords; %] @@ -20,7 +20,7 @@ <input type="hidden" name="latitude" value="[% problem.latitude %]"> </p> <p> - <a href="https://www.google.com/maps/dir/Current+Location/[% problem.latitude %],[% problem.longitude %]" class="btn btn--block btn--navigate">[% loc('Navigate to this problem') %]</a> + <a target="_blank" href="https://www.google.com/maps/dir/Current+Location/[% problem.latitude %],[% problem.longitude %]" class="btn btn--block btn--navigate">[% loc('Navigate to this problem') %]</a> </p> <p> <a href="#" class="btn btn--block btn--geolocate" id="geolocate_link">[% loc('Set to my current location') %]</a> @@ -52,30 +52,49 @@ [% cat_prefix = category | lower | replace('[^a-z]', '') %] [% cat_prefix = "category_" _ cat_prefix _ "_" %] [% IF category == problem.category %] - <p data-category="[% category | html %]"> + <p data-category="[% category | html %]" data-priorities="[% priorities_by_category.$category %]"> [% INCLUDE 'report/new/category_extras_fields.html' %] </p> - [% ELSIF category_extras.$category.size %] - <p data-category="[% category | html %]" class="hidden"> + [% ELSE %] + <p data-category="[% category | html %]" class="hidden" data-priorities="[% priorities_by_category.$category %]"> [% INCLUDE 'report/new/category_extras_fields.html' report_meta='' %] </p> [% END %] [% END %] [% IF permissions.report_inspect %] - <p> - <label for="state">[% loc('State') %]</label> - [%# XXX this is duplicated from admin/report_edit.html, should be refactored %] - <select name="state" id="state" class="form-control"> - [% FOREACH group IN state_groups %] - <optgroup label="[% group.0 %]"> - [% FOREACH state IN group.1 %] - <option [% 'selected ' IF state == problem.state %] value="[% state %]">[% state_pretty.$state %]</option> - [% END %] - </optgroup> - [% END %] - </select> - </p> + <p> + <label for="state">[% loc('State') %]</label> + [%# XXX this is duplicated from admin/report_edit.html, should be refactored %] + <select name="state" id="state" class="form-control"> + [% FOREACH group IN state_groups %] + <optgroup label="[% group.0 %]"> + [% FOREACH state IN group.1 %] + <option [% 'selected ' IF state == problem.state %] value="[% state %]">[% state_pretty.$state %]</option> + [% END %] + </optgroup> + [% END %] + </select> + </p> + <div id="js-duplicate-reports" class="[% "hidden" UNLESS problem.duplicate_of %]"> + <input type="hidden" name="duplicate_of" value="[% problem.duplicate_of.id %]"> + <p class="[% "hidden" UNLESS problem.duplicate_of %]"><strong>[% loc('Duplicate of') %]</strong></p> + <p class="[% "hidden" IF problem.duplicate_of %]">[% loc('Which report is it a duplicate of?') %]</p> + <ul class="item-list"> + [% IF problem.duplicate_of %] + [% INCLUDE 'report/_item.html' item_extra_class = 'item-list--reports__item--selected' problem = problem.duplicate_of %] + <li class="item-list__item"><a class="btn" href="#" id="js-change-duplicate-report">[% loc('Choose another') %]</a></li> + [% END %] + </ul> + </div> + [% IF problem.duplicates.size %] + <p><strong>[% loc('Duplicates') %]</strong></p> + <ul class="item-list"> + [% FOR duplicate IN problem.duplicates %] + [% INCLUDE 'report/_item.html' problem = duplicate %] + [% END %] + </ul> + [% END %] [% END %] </div> @@ -88,7 +107,7 @@ <select name="priority" id="problem_priority" class="form-control"> <option value="" [% 'selected' UNLESS problem.response_priority_id %]>-</option> [% FOREACH priority IN problem.response_priorities %] - <option value="[% priority.id %]" [% 'selected' IF problem.response_priority_id == priority.id %] [% 'disabled' IF priority.deleted %]>[% priority.name %]</option> + <option value="[% priority.id %]" [% 'selected' IF problem.response_priority_id == priority.id %] [% 'disabled' IF priority.deleted %]>[% priority.name | html %]</option> [% END %] </select> </p> @@ -139,7 +158,7 @@ <p> <input type="hidden" name="token" value="[% csrf_token %]"> <a class="btn" href="[% c.uri_for( '/report', problem.id ) %]">[% loc('Cancel') %]</a> - <input class="btn btn-primary" type="submit" value="[% loc('Save changes') %]" name="save" /> + <input class="btn btn-primary" type="submit" value="[% loc('Save changes') %]" data-value-original="[% loc('Save changes') %]" data-value-duplicate="[% loc('Save + close as duplicate') %]" name="save" /> </p> </div> diff --git a/templates/web/base/report/_item.html b/templates/web/base/report/_item.html index 5894c5d81..0f42b00ce 100644 --- a/templates/web/base/report/_item.html +++ b/templates/web/base/report/_item.html @@ -1,4 +1,4 @@ -<li class="item-list__item item-list--reports__item [% item_extra_class %]"> +<li class="item-list__item item-list--reports__item [% item_extra_class %]" data-report-id="[% problem.id | html %]"> <a href="[% c.cobrand.base_url_for_report( problem ) %][% problem.url %]"> [% IF problem.photo %] <img class="img" height="60" width="90" src="[% problem.photos.first.url_fp %]" alt=""> diff --git a/templates/web/base/report/display.html b/templates/web/base/report/display.html index 1ee5c4636..7c26c4938 100644 --- a/templates/web/base/report/display.html +++ b/templates/web/base/report/display.html @@ -40,12 +40,19 @@ [% INCLUDE 'report/banner.html' %] [% INCLUDE 'report/_main.html' %] + +[% IF problem.duplicate_of %] + [% INCLUDE 'report/duplicate-no-updates.html' hide_header = 1 %] +[% END %] + [% TRY %][% INCLUDE 'report/_message_manager.html' %][% CATCH file %][% END %] [% INCLUDE 'report/display_tools.html' %] [% TRY %][% INCLUDE 'report/sharing.html' %][% CATCH file %][% END %] [% INCLUDE 'report/updates.html' %] -[% IF NOT shown_form %] +[% IF problem.duplicate_of %] + [% INCLUDE 'report/duplicate-no-updates.html' %] +[% ELSIF NOT shown_form %] [% INCLUDE 'report/update-form.html' %] [% END %] diff --git a/templates/web/base/report/display_tools.html b/templates/web/base/report/display_tools.html index f27ed8da5..fb337df59 100644 --- a/templates/web/base/report/display_tools.html +++ b/templates/web/base/report/display_tools.html @@ -6,7 +6,9 @@ <input type="submit" id="key-tool-report-abuse" class="abuse btn" data-confirm="[% loc('Are you sure?') %]" name="remove_from_site" value="[% loc('Remove from site') %]"> </form></li> [% ELSIF c.cobrand.moniker != 'zurich' %] - <li><a rel="nofollow" id="key-tool-report-abuse" class="abuse" href="[% c.uri_for( '/contact', { id => problem.id } ) %]">[% loc('Report abuse' ) %]</a></li> + <li><a rel="nofollow" id="key-tool-report-abuse" class="abuse" href="[% c.uri_for( '/contact', { id => problem.id } ) %]">[% + c.cobrand.moniker == 'fixmystreet' ? 'Unsuitable?' : loc('Report abuse') + %]</a></li> [% END %] [% IF c.cobrand.moniker != 'zurich' %] <li><a rel="nofollow" id="key-tool-report-updates" class="feed" href="[% c.uri_for( '/alert/subscribe', { id => problem.id } ) %]">[% loc('Get updates' ) %]</a></li> diff --git a/templates/web/base/report/duplicate-no-updates.html b/templates/web/base/report/duplicate-no-updates.html new file mode 100644 index 000000000..eb9ded728 --- /dev/null +++ b/templates/web/base/report/duplicate-no-updates.html @@ -0,0 +1,7 @@ +<div> + [% UNLESS hide_header %]<h2>[% loc( 'Provide an update') %]</h2>[% END %] + <p>[% loc("This report is a duplicate. Please leave updates on the original report:") %]</p> + <ul class="item-list"> + [% INCLUDE 'report/_item.html' item_extra_class = 'item-list__item--with-pin item-list--reports__item--selected' problem = problem.duplicate_of %] + </ul> +</div> diff --git a/templates/web/base/report/updates.html b/templates/web/base/report/updates.html index fc2ac6c78..ff48ecbca 100644 --- a/templates/web/base/report/updates.html +++ b/templates/web/base/report/updates.html @@ -54,7 +54,7 @@ [%- ELSIF state == 'not responsible' %] [%- update_state = loc( "marked as not the council's responsibility" ) %] [%- ELSIF state == 'duplicate' %] - [%- update_state = loc( 'marked as a duplicate report' ) %] + [%- update_state = loc( 'closed as a duplicate report' ) %] [%- ELSIF state == 'internal referral' %] [%- update_state = loc( 'marked as an internal referral' ) %] [%- END %] diff --git a/templates/web/base/reports/_list-filters.html b/templates/web/base/reports/_list-filters.html index 5ca483a1f..9c2a74e57 100644 --- a/templates/web/base/reports/_list-filters.html +++ b/templates/web/base/reports/_list-filters.html @@ -1,5 +1,5 @@ [% select_status = BLOCK %] - <select class="form-control" name="status" id="statuses" multiple data-all="[% loc('All reports') %]"> + <select class="form-control js-multiple" name="status" id="statuses" multiple data-all="[% loc('All reports') %]"> <option value="open"[% ' selected' IF filter_status.open %]>[% loc('Unfixed reports') %]</option> <option value="closed"[% ' selected' IF filter_status.closed %]>[% loc('Closed reports') %]</option> <option value="fixed"[% ' selected' IF filter_status.fixed %]>[% loc('Fixed reports') %]</option> @@ -8,7 +8,7 @@ [% select_category = BLOCK %] [% IF filter_categories.size %] - <select class="form-control" name="filter_category" id="filter_categories" multiple data-all="[% loc('Everything') %]"> + <select class="form-control js-multiple" name="filter_category" id="filter_categories" multiple data-all="[% loc('Everything') %]"> [% FOR cat IN filter_categories %] <option value="[% cat | html %]"[% ' selected' IF filter_category.grep(cat).size %]> [% cat | html %] diff --git a/templates/web/bromley/maps/bromley.html b/templates/web/bromley/maps/bromley.html new file mode 100644 index 000000000..aa5789c1c --- /dev/null +++ b/templates/web/bromley/maps/bromley.html @@ -0,0 +1,9 @@ +[% map_js = BLOCK %] +<script type="text/javascript" src="[% version('/js/OpenLayers/OpenLayers.fixmystreet.js') %]"></script> +<script type="text/javascript" src="[% version('/js/map-OpenLayers.js') %]"></script> +<script type="text/javascript" src="[% version('/js/map-bing-ol.js') %]"></script> +<script type="text/javascript" src="[% version('/js/map-fms.js') %]"></script> +<script type="text/javascript" src="[% version('/cobrands/bromley/map.js') %]"></script> +[% END %] + +[% map_html = INCLUDE maps/openlayers.html include_key = 1 %] diff --git a/templates/web/bromley/report/display.html b/templates/web/bromley/report/display.html index 9f2089d28..4c1a69bca 100644 --- a/templates/web/bromley/report/display.html +++ b/templates/web/bromley/report/display.html @@ -21,147 +21,19 @@ [% INCLUDE 'report/banner.html' %] [% INCLUDE 'report/_main.html' %] -[% INCLUDE 'report/display_tools.html' %] -[% INCLUDE 'report/updates.html' %] - -<div id="update_form"> - <h2>[% loc( 'Provide an update') %]</h2> - - [% INCLUDE 'errors.html' %] - - <form method="post" action="[% c.uri_for( '/report/update' ) %]" name="updateForm" class="validate"[% IF c.cobrand.allow_photo_upload %] enctype="multipart/form-data"[% END %]> - <input type="hidden" name="token" value="[% csrf_token %]"> - <fieldset> - <input type="hidden" name="submit_update" value="1"> - <input type="hidden" name="id" value="[% problem.id | html %]"> - - [% IF c.cobrand.allow_photo_upload %] - <input type="hidden" name="upload_fileid" value="[% upload_fileid %]"> - <label for="form_photo"> - <span data-singular="[% loc('Photo') %]" data-plural="[% loc('Photos') %]">[% loc('Photo') %]</span> - </label> - - [% IF field_errors.photo %] - <p class='form-error'>[% field_errors.photo %]</p> - [% END %] - - <div id="form_photos"> - [% IF upload_fileid %] - <p>[% loc('You have already attached photos to this update. Note that you can attach a maximum of 3 to this update (if you try to upload more, the oldest will be removed).') %]</p> - [% FOREACH id IN upload_fileid.split(',') %] - <img align="right" src="/photo/temp.[% id %]" alt=""> - [% END %] - [% END %] - <input type="file" name="photo1" id="form_photo"> - <label for="form_photo2">[% loc('Photo') %]</label> - <input type="file" name="photo2" id="form_photo2"> - <label for="form_photo3">[% loc('Photo') %]</label> - <input type="file" name="photo3" id="form_photo3"> - </div> - [% END %] - - <label for="form_update">[% loc( 'Update' ) %]</label> - [% IF field_errors.update %] - <div class='form-error'>[% field_errors.update %]</div> - [% END %] - <textarea class="form-control" rows="7" cols="30" name="update" id="form_update" placeholder="[% loc('Please write your update here') %]" required>[% update.text | html %]</textarea> - - <div class="general-notes"> - <p>Please note this comments box can only be used for this report. - <br><a href="http://www.bromley.gov.uk/report">Report a different issue</a> - </div> - - [% IF c.user && c.user.belongs_to_body( problem.bodies_str ) %] - <label for="form_state">[% loc( 'State' ) %]</label> - <select name="state" id="form_state" class="form-control"> - [% FOREACH state IN [ ['confirmed', loc('Open')], ['investigating', - loc('Investigating')], ['action scheduled', loc('Action Scheduled')], - ['in progress', loc('In Progress')], ['duplicate', loc('Duplicate')], - ['unable to fix', loc('Unable to fix')], ['not responsible', loc('Not Responsible')], - ['fixed', loc('Fixed')] ] %] - <option [% 'selected ' IF state.0 == problem.state_display %] value="[% state.0 %]">[% state.1 %]</option> - [% END %] - </select> - [% ELSE %] - [% IF problem.is_fixed AND c.user_exists AND c.user.id == problem.user_id %] - - <input type="checkbox" name="reopen" id="form_reopen" value="1"[% ' checked' IF update.mark_open %]> - <label class="inline" for="form_reopen">[% loc('This problem has not been fixed') %]</label> - - [% ELSIF !problem.is_fixed %] - - <div class="checkbox-group"> - <input type="checkbox" name="fixed" id="form_fixed" value="1"[% ' checked' IF update.mark_fixed %]> - <label class="inline" for="form_fixed">[% loc('This problem has been fixed') %]</label> - </div> - - [% END %] - [% END %] - - [% IF c.user_exists %] - - [% INCLUDE name %] - - <input class="final-submit green-btn js-submit_register" type="submit" name="submit_register" value="[% loc('Post') %]"> +[% IF problem.duplicate_of %] + [% INCLUDE 'report/duplicate-no-updates.html' hide_header = 1 %] +[% END %] - [% ELSE %] - - <label for="form_rznvy">[% loc('Email' ) %] - <span class="muted">([% loc('We never show your email') %])</span> - </label> - - [% IF field_errors.email %] - <p class='form-error'>[% field_errors.email %]</p> - [% END %] - <input class="form-control" type="email" name="rznvy" id="form_rznvy" value="[% update.user.email | html %]" placeholder="[% loc('Your email address' ) %]" required> - - <div id="form_sign_in"> - <p>To submit your update you now need to confirm it either by email or by using a FixMyStreet password.</p> - - <div id="form_sign_in_no" class="form-box"> - <h5>Confirm my report by email</h5> - - [% INCLUDE name %] - - <label for="password_register">[% loc('Password (optional)') %]</label> - - <div class="general-notes"> - <p>[% loc('Providing a password is optional, but doing so will allow you to more easily report problems, leave updates and manage your reports.') %]</p> - </div> - - <div class="form-txt-submit-box"> - <input type="password" class="form-control" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]"> - <input class="green-btn js-submit_register" type="submit" name="submit_register" value="[% loc('Post') %]"> - </div> - </div> - <div id="form_sign_in_yes" class="form-box"> - <h5>Confirm my report with my FixMyStreet password</h5> - - <label class="hidden-js n" for="password_sign_in">[% loc('Yes I have a password') %]</label> - [% IF field_errors.password %] - <p class='form-error'>[% field_errors.password %]</p> - [% END %] - <div class="form-txt-submit-box"> - <input type="password" class="form-control" name="password_sign_in" id="password_sign_in" value="" placeholder="[% loc('Your password') %]"> - <input class="green-btn js-submit_sign_in" type="submit" name="submit_sign_in" value="[% loc('Post') %]"> - </div> - - <div class="checkbox-group"> - <input type="checkbox" id="remember_me" name="remember_me" value='1'[% ' checked' IF remember_me %]> - <label class="inline n" for="remember_me">[% loc('Keep me signed in on this computer') %]</label> - </div> - </div> - </div> - - [% END %] - - <p>Your information will only be used in accordance with our <a href="/faq#privacy">privacy policy</a>.</p> - - </fieldset> - </form> -</div> +[% INCLUDE 'report/display_tools.html' %] +[% INCLUDE 'report/updates.html' %] +[% IF problem.duplicate_of %] + [% INCLUDE 'report/duplicate-no-updates.html' %] +[% ELSE %] + [% INCLUDE 'report/update-form.html' %] +[% END %] </div> [% INCLUDE 'footer.html' %] diff --git a/templates/web/bromley/report/update-form.html b/templates/web/bromley/report/update-form.html new file mode 100644 index 000000000..112cb2960 --- /dev/null +++ b/templates/web/bromley/report/update-form.html @@ -0,0 +1,137 @@ +<div id="update_form"> + <h2>[% loc( 'Provide an update') %]</h2> + + [% INCLUDE 'errors.html' %] + + <form method="post" action="[% c.uri_for( '/report/update' ) %]" name="updateForm" class="validate"[% IF c.cobrand.allow_photo_upload %] enctype="multipart/form-data"[% END %]> + <input type="hidden" name="token" value="[% csrf_token %]"> + <fieldset> + <input type="hidden" name="submit_update" value="1"> + <input type="hidden" name="id" value="[% problem.id | html %]"> + + [% IF c.cobrand.allow_photo_upload %] + <input type="hidden" name="upload_fileid" value="[% upload_fileid %]"> + <label for="form_photo"> + <span data-singular="[% loc('Photo') %]" data-plural="[% loc('Photos') %]">[% loc('Photo') %]</span> + </label> + + [% IF field_errors.photo %] + <p class='form-error'>[% field_errors.photo %]</p> + [% END %] + + <div id="form_photos"> + [% IF upload_fileid %] + <p>[% loc('You have already attached photos to this update. Note that you can attach a maximum of 3 to this update (if you try to upload more, the oldest will be removed).') %]</p> + [% FOREACH id IN upload_fileid.split(',') %] + <img align="right" src="/photo/temp.[% id %]" alt=""> + [% END %] + [% END %] + <input type="file" name="photo1" id="form_photo"> + <label for="form_photo2">[% loc('Photo') %]</label> + <input type="file" name="photo2" id="form_photo2"> + <label for="form_photo3">[% loc('Photo') %]</label> + <input type="file" name="photo3" id="form_photo3"> + </div> + [% END %] + + <label for="form_update">[% loc( 'Update' ) %]</label> + [% IF field_errors.update %] + <div class='form-error'>[% field_errors.update %]</div> + [% END %] + <textarea class="form-control" rows="7" cols="30" name="update" id="form_update" placeholder="[% loc('Please write your update here') %]" required>[% update.text | html %]</textarea> + + <div class="general-notes"> + <p>Please note this comments box can only be used for this report. + <br><a href="http://www.bromley.gov.uk/report">Report a different issue</a> + </div> + + [% IF c.user && c.user.belongs_to_body( problem.bodies_str ) %] + <label for="form_state">[% loc( 'State' ) %]</label> + <select name="state" id="form_state" class="form-control"> + [% FOREACH state IN [ ['confirmed', loc('Open')], ['investigating', + loc('Investigating')], ['action scheduled', loc('Action Scheduled')], + ['in progress', loc('In Progress')], ['duplicate', loc('Duplicate')], + ['unable to fix', loc('Unable to fix')], ['not responsible', loc('Not Responsible')], + ['fixed', loc('Fixed')] ] %] + <option [% 'selected ' IF state.0 == problem.state_display %] value="[% state.0 %]">[% state.1 %]</option> + [% END %] + </select> + [% ELSE %] + [% IF problem.is_fixed AND c.user_exists AND c.user.id == problem.user_id %] + + <input type="checkbox" name="reopen" id="form_reopen" value="1"[% ' checked' IF update.mark_open %]> + <label class="inline" for="form_reopen">[% loc('This problem has not been fixed') %]</label> + + [% ELSIF !problem.is_fixed %] + + <div class="checkbox-group"> + <input type="checkbox" name="fixed" id="form_fixed" value="1"[% ' checked' IF update.mark_fixed %]> + <label class="inline" for="form_fixed">[% loc('This problem has been fixed') %]</label> + </div> + + [% END %] + [% END %] + + [% IF c.user_exists %] + + [% INCLUDE name %] + + <input class="final-submit green-btn js-submit_register" type="submit" name="submit_register" value="[% loc('Post') %]"> + + + [% ELSE %] + + <label for="form_rznvy">[% loc('Email' ) %] + <span class="muted">([% loc('We never show your email') %])</span> + </label> + + [% IF field_errors.email %] + <p class='form-error'>[% field_errors.email %]</p> + [% END %] + <input class="form-control" type="email" name="rznvy" id="form_rznvy" value="[% update.user.email | html %]" placeholder="[% loc('Your email address' ) %]" required> + + <div id="form_sign_in"> + <p>To submit your update you now need to confirm it either by email or by using a FixMyStreet password.</p> + + <div id="form_sign_in_no" class="form-box"> + <h5>Confirm my report by email</h5> + + [% INCLUDE name %] + + <label for="password_register">[% loc('Password (optional)') %]</label> + + <div class="general-notes"> + <p>[% loc('Providing a password is optional, but doing so will allow you to more easily report problems, leave updates and manage your reports.') %]</p> + </div> + + <div class="form-txt-submit-box"> + <input type="password" class="form-control" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]"> + <input class="green-btn js-submit_register" type="submit" name="submit_register" value="[% loc('Post') %]"> + </div> + </div> + <div id="form_sign_in_yes" class="form-box"> + <h5>Confirm my report with my FixMyStreet password</h5> + + <label class="hidden-js n" for="password_sign_in">[% loc('Yes I have a password') %]</label> + [% IF field_errors.password %] + <p class='form-error'>[% field_errors.password %]</p> + [% END %] + <div class="form-txt-submit-box"> + <input type="password" class="form-control" name="password_sign_in" id="password_sign_in" value="" placeholder="[% loc('Your password') %]"> + <input class="green-btn js-submit_sign_in" type="submit" name="submit_sign_in" value="[% loc('Post') %]"> + </div> + + <div class="checkbox-group"> + <input type="checkbox" id="remember_me" name="remember_me" value='1'[% ' checked' IF remember_me %]> + <label class="inline n" for="remember_me">[% loc('Keep me signed in on this computer') %]</label> + </div> + </div> + </div> + + [% END %] + + <p>Your information will only be used in accordance with our <a href="/faq#privacy">privacy policy</a>.</p> + + </fieldset> + </form> +</div> diff --git a/templates/web/fixmystreet.com/about/council.html b/templates/web/fixmystreet.com/about/council.html index 651e4f57f..6814f70c3 100644 --- a/templates/web/fixmystreet.com/about/council.html +++ b/templates/web/fixmystreet.com/about/council.html @@ -163,7 +163,7 @@ <h3>Top‐to‐bottom case‐management</h3> <p class="pricing-table__item__price"><strong>£35,000 per year</strong></p> <p>Starting from zero? Want a completely new system from beginning to end? Then opt for the full-service option. </p> - <p>Use FixMyStreet to manage reports every step of the way. This system handles from everything, from acknowledgement, through inspection to contractor instruction or resolution. Each element was co-designed with local government insiders who know exactly what’s needed for clear, simple highways parks and street case management.</p> + <p>Use FixMyStreet to manage reports every step of the way. This system handles everything, from acknowledgement, through inspection to contractor instruction or resolution. Each element was co-designed with local government insiders who know exactly what’s needed for clear, simple highways parks and street case management.</p> </div> </div> diff --git a/templates/web/fixmystreet.com/contact/unsuitable-text.html b/templates/web/fixmystreet.com/contact/unsuitable-text.html new file mode 100644 index 000000000..92642aec7 --- /dev/null +++ b/templates/web/fixmystreet.com/contact/unsuitable-text.html @@ -0,0 +1,7 @@ +<p> + [% loc('Does this report break our <a href="/about/house-rules">House Rules</a>? Use this form to let us know.') %] +</p> + +<p> + <strong>[% loc('If you are trying to make a new report, please <a href="/">go to the front page</a> and follow the instructions.') %]</strong> +</p> diff --git a/templates/web/oxfordshire/about/faq-en-gb.html b/templates/web/oxfordshire/about/faq-en-gb.html index b85943e0d..c88634e11 100755 --- a/templates/web/oxfordshire/about/faq-en-gb.html +++ b/templates/web/oxfordshire/about/faq-en-gb.html @@ -28,7 +28,7 @@ </p> <p> If you are reporting an emergency please do not report it online, but ring our - Customer Service Centre on <strong>0845 310 1111</strong>. + Customer Service Centre on <strong>0345 310 1111</strong>. </p> <p> Here is a <a href="http://vimeo.com/65813425">short video</a> @@ -90,7 +90,7 @@ <dt><a name="pothole"></a>Should I report a pothole on this site, or call?</dt> <dd> <p> - <strong>Please call us on 0845 310 1111 for urgent enquiries.</strong> + <strong>Please call us on 0345 310 1111 for urgent enquiries.</strong> </p> <p> Potholes may need urgent attention if they are more than 40 millimetres in depth and/or diff --git a/web/cobrands/bromley/map.js b/web/cobrands/bromley/map.js new file mode 100644 index 000000000..0753907cc --- /dev/null +++ b/web/cobrands/bromley/map.js @@ -0,0 +1 @@ +fixmystreet.maps.tile_base = [ [ "", "a-" ], "https://{S}fix.bromley.gov.uk/tilma" ]; diff --git a/web/cobrands/fixmystreet/fixmystreet.js b/web/cobrands/fixmystreet/fixmystreet.js index fed983a2c..e4c5ba71c 100644 --- a/web/cobrands/fixmystreet/fixmystreet.js +++ b/web/cobrands/fixmystreet/fixmystreet.js @@ -390,6 +390,87 @@ $.extend(fixmystreet.set_up, { }); }, + manage_duplicates: function() { + // Deal with changes to report state by inspector/other staff, specifically + // displaying nearby reports if it's changed to 'duplicate'. + function refresh_duplicate_list() { + var report_id = $("#report_inspect_form .js-report-id").text(); + var args = { + filter_category: $("#report_inspect_form select#category").val(), + latitude: $('input[name="latitude"]').val(), + longitude: $('input[name="longitude"]').val() + }; + $("#js-duplicate-reports ul").html("<li>Loading...</li>"); + var nearby_url = '/report/'+report_id+'/nearby.json'; + $.getJSON(nearby_url, args, function(data) { + var duplicate_of = $("#report_inspect_form [name=duplicate_of]").val(); + var $reports = $(data.current) + .filter("li") + .not("[data-report-id="+report_id+"]") + .slice(0, 5); + $reports.filter("[data-report-id="+duplicate_of+"]").addClass("item-list--reports__item--selected"); + + (function() { + var timeout; + $reports.on('mouseenter', function(){ + clearTimeout(timeout); + fixmystreet.maps.markers_highlight(parseInt($(this).data('reportId'), 10)); + }).on('mouseleave', function(){ + timeout = setTimeout(fixmystreet.maps.markers_highlight, 50); + }); + })(); + + $("#js-duplicate-reports ul").empty().prepend($reports); + + $reports.find("a").click(function() { + var report_id = $(this).closest("li").data('reportId'); + $("#report_inspect_form [name=duplicate_of]").val(report_id); + $("#js-duplicate-reports ul li").removeClass("item-list--reports__item--selected"); + $(this).closest("li").addClass("item-list--reports__item--selected"); + return false; + }); + + show_nearby_pins(data, report_id); + }); + } + + function show_nearby_pins(data, report_id) { + var markers = fixmystreet.maps.markers_list( data.pins, true ); + // We're replacing all the features in the markers layer with the + // possible duplicates, but the list of pins from the server doesn't + // include the current report. So we need to extract the feature for + // the current report and include it in the list of features we're + // showing on the layer. + var report_marker = fixmystreet.maps.get_marker_by_id(parseInt(report_id, 10)); + if (report_marker) { + markers.unshift(report_marker); + } + fixmystreet.markers.removeAllFeatures(); + fixmystreet.markers.addFeatures( markers ); + } + + function state_change() { + // The duplicate report list only makes sense when state is 'duplicate' + if ($(this).val() !== "duplicate") { + $("#js-duplicate-reports").addClass("hidden"); + return; + } else { + $("#js-duplicate-reports").removeClass("hidden"); + } + // If this report is already marked as a duplicate of another, then + // there's no need to refresh the list of duplicate reports + var duplicate_of = $("#report_inspect_form [name=duplicate_of]").val(); + if (!!duplicate_of) { + return; + } + + refresh_duplicate_list(); + } + + $("#report_inspect_form").on("change.state", "select#state", state_change); + $("#js-change-duplicate-report").click(refresh_duplicate_list); + }, + contribute_as: function() { $('.content').on('change', '.js-contribute-as', function(){ @@ -563,6 +644,9 @@ $.extend(fixmystreet.set_up, { return; } + // Focus on form + $('html,body').scrollTop($('#report_inspect_form').offset().top); + // On the manage/inspect report form, we already have all the extra inputs // in the DOM, we just need to hide/show them as appropriate. $('form#report_inspect_form [name=category]').change(function() { @@ -570,8 +654,33 @@ $.extend(fixmystreet.set_up, { selector = "[data-category='" + category + "']"; $("form#report_inspect_form [data-category]:not(" + selector + ")").addClass("hidden"); $("form#report_inspect_form " + selector).removeClass("hidden"); + // And update the associated priority list + var priorities = $("form#report_inspect_form " + selector).data('priorities'); + var $select = $('#problem_priority'), + curr_pri = $select.val(); + $select.find('option:gt(0)').remove(); + $.each(priorities.split('&'), function(i, kv) { + if (!kv) { + return; + } + kv = kv.split('=', 2); + $select.append($('<option>', { value: kv[0], text: decodeURIComponent(kv[1]) })); + }); + $select.val(curr_pri); }); + // The inspect form submit button can change depending on the selected state + $("#report_inspect_form [name=state]").change(function(){ + var state = $(this).val(); + var $submit = $("#report_inspect_form input[type=submit]"); + var value = $submit.attr('data-value-'+state); + if (value !== undefined) { + $submit.val(value); + } else { + $submit.val($submit.data('valueOriginal')); + } + }).change(); + $('.js-toggle-public-update').each(function() { var $checkbox = $(this); var toggle_public_update = function() { @@ -943,7 +1052,7 @@ fixmystreet.update_pin = function(lonlat, savePushState) { // something from it, then pre-fill the category field in the report, // if it's a value already present in the drop-down. var category = $("#filter_categories").val(); - if (category !== undefined && $("#form_category option[value="+category+"]").length) { + if (category !== undefined && $("#form_category option[value='"+category+"']").length) { $("#form_category").val(category); } @@ -1090,6 +1199,7 @@ fixmystreet.display = { $twoColReport.appendTo('#map_sidebar'); $('body').addClass('with-actions'); fixmystreet.set_up.report_page_inspect(); + fixmystreet.set_up.manage_duplicates(); } else { $sideReport.appendTo('#map_sidebar'); } diff --git a/web/cobrands/sass/_base.scss b/web/cobrands/sass/_base.scss index f3753905b..7fb0c797f 100644 --- a/web/cobrands/sass/_base.scss +++ b/web/cobrands/sass/_base.scss @@ -289,7 +289,7 @@ select.form-control { &[multiple] { height: auto; } - .js &[multiple] { + .js &.js-multiple[multiple] { height: 2.2em; } } diff --git a/web/cobrands/sass/_report_list_pins.scss b/web/cobrands/sass/_report_list_pins.scss index 74f0a5f90..eaeefbc10 100644 --- a/web/cobrands/sass/_report_list_pins.scss +++ b/web/cobrands/sass/_report_list_pins.scss @@ -50,6 +50,14 @@ color: #777; } } +.item-list--reports__item--selected { + background: $base_bg; + + a, a:hover, a:focus { + background-color: transparent; + } +} + .item-list__item--empty { background: none; diff --git a/web/js/OpenLayers/OpenLayers.fixmystreet.js b/web/js/OpenLayers/OpenLayers.fixmystreet.js index cfa4c2bee..df0a88c98 100644 --- a/web/js/OpenLayers/OpenLayers.fixmystreet.js +++ b/web/js/OpenLayers/OpenLayers.fixmystreet.js @@ -225,8 +225,8 @@ if(g)for(var h=0;!f&&h<g.length;){var k=g[h];if(k.name==b&&k.observer==c&&k.useC !1}},CLASS_NAME:"OpenLayers.Event"};OpenLayers.Event.observe(window,"unload",OpenLayers.Event.unloadCache,!1); OpenLayers.Events=OpenLayers.Class({BROWSER_EVENTS:"mouseover mouseout mousedown mouseup mousemove click dblclick rightclick dblrightclick resize focus blur touchstart touchmove touchend keydown".split(" "),TOUCH_MODEL_POINTER:"pointer",TOUCH_MODEL_MSPOINTER:"MSPointer",TOUCH_MODEL_TOUCH:"touch",listeners:null,object:null,element:null,eventHandler:null,fallThrough:null,includeXY:!1,extensions:null,extensionCount:null,clearMouseListener:null,initialize:function(a,b,c,d,e){OpenLayers.Util.extend(this, e);this.object=a;this.fallThrough=d;this.listeners={};this.extensions={};this.extensionCount={};this._pointerTouches=[];null!=b&&this.attachToElement(b)},destroy:function(){for(var a in this.extensions)"boolean"!==typeof this.extensions[a]&&this.extensions[a].destroy();this.extensions=null;this.element&&(OpenLayers.Event.stopObservingElement(this.element),this.element.hasScrollEvent&&OpenLayers.Event.stopObserving(window,"scroll",this.clearMouseListener));this.eventHandler=this.fallThrough=this.object= -this.listeners=this.element=null},addEventType:function(a){},attachToElement:function(a){this.element?OpenLayers.Event.stopObservingElement(this.element):(this.eventHandler=OpenLayers.Function.bindAsEventListener(this.handleBrowserEvent,this),this.clearMouseListener=OpenLayers.Function.bind(this.clearMouseCache,this));this.element=a;for(var b=this.getTouchModel(),c,d=0,e=this.BROWSER_EVENTS.length;d<e;d++)c=this.BROWSER_EVENTS[d],OpenLayers.Event.observe(a,c,this.eventHandler),b!==this.TOUCH_MODEL_POINTER&& -b!==this.TOUCH_MODEL_MSPOINTER||0!==c.indexOf("touch")||this.addPointerTouchListener(a,c,this.eventHandler);OpenLayers.Event.observe(a,"dragstart",OpenLayers.Event.stop)},on:function(a){for(var b in a)"scope"!=b&&a.hasOwnProperty(b)&&this.register(b,a.scope,a[b])},register:function(a,b,c,d){a in OpenLayers.Events&&!this.extensions[a]&&(this.extensions[a]=new OpenLayers.Events[a](this));if(null!=c){null==b&&(b=this.object);var e=this.listeners[a];e||(e=[],this.listeners[a]=e,this.extensionCount[a]= +this.listeners=this.element=null},addEventType:function(a){},attachToElement:function(a){this.element?OpenLayers.Event.stopObservingElement(this.element):(this.eventHandler=OpenLayers.Function.bindAsEventListener(this.handleBrowserEvent,this),this.clearMouseListener=OpenLayers.Function.bind(this.clearMouseCache,this));this.element=a;for(var b=this.getTouchModel(),c,d=0,e=this.BROWSER_EVENTS.length;d<e;d++){c=this.BROWSER_EVENTS[d];(b===this.TOUCH_MODEL_POINTER|| +b===this.TOUCH_MODEL_MSPOINTER)&&0===c.indexOf("touch")?this.addPointerTouchListener(a,c,this.eventHandler):OpenLayers.Event.observe(a,c,this.eventHandler);}OpenLayers.Event.observe(a,"dragstart",OpenLayers.Event.stop)},on:function(a){for(var b in a)"scope"!=b&&a.hasOwnProperty(b)&&this.register(b,a.scope,a[b])},register:function(a,b,c,d){a in OpenLayers.Events&&!this.extensions[a]&&(this.extensions[a]=new OpenLayers.Events[a](this));if(null!=c){null==b&&(b=this.object);var e=this.listeners[a];e||(e=[],this.listeners[a]=e,this.extensionCount[a]= 0);b={obj:b,func:c};d?(e.splice(this.extensionCount[a],0,b),"object"===typeof d&&d.extension&&this.extensionCount[a]++):e.push(b)}},registerPriority:function(a,b,c){this.register(a,b,c,!0)},un:function(a){for(var b in a)"scope"!=b&&a.hasOwnProperty(b)&&this.unregister(b,a.scope,a[b])},unregister:function(a,b,c){null==b&&(b=this.object);a=this.listeners[a];if(null!=a)for(var d=0,e=a.length;d<e;d++)if(a[d].obj==b&&a[d].func==c){a.splice(d,1);break}},remove:function(a){null!=this.listeners[a]&&(this.listeners[a]= [])},triggerEvent:function(a,b){var c=this.listeners[a];if(c&&0!=c.length){null==b&&(b={});b.object=this.object;b.element=this.element;b.type||(b.type=a);for(var c=c.slice(),d,e=0,f=c.length;e<f&&(d=c[e],d=d.func.apply(d.obj,[b]),void 0==d||0!=d);e++);this.fallThrough||OpenLayers.Event.stop(b,!0);return d}},handleBrowserEvent:function(a){var b=a.type,c=this.listeners[b];if(c&&0!=c.length){if((c=a.touches)&&c[0]){for(var d=0,e=0,f=c.length,g,h=0;h<f;++h)g=this.getTouchClientXY(c[h]),d+=g.clientX,e+= g.clientY;a.clientX=d/f;a.clientY=e/f}this.includeXY&&(a.xy=this.getMousePosition(a));this.triggerEvent(b,a)}},getTouchClientXY:function(a){var b=window.olMockWin||window,c=b.pageXOffset,b=b.pageYOffset,d=a.clientX,e=a.clientY;if(0===a.pageY&&Math.floor(e)>Math.floor(a.pageY)||0===a.pageX&&Math.floor(d)>Math.floor(a.pageX))d-=c,e-=b;else if(e<a.pageY-b||d<a.pageX-c)d=a.pageX-c,e=a.pageY-b;a.olClientX=d;a.olClientY=e;return{clientX:d,clientY:e}},clearMouseCache:function(){this.element.scrolls=null; diff --git a/web/js/OpenLayers/theme/default/style.css b/web/js/OpenLayers/theme/default/style.css index cbed84e8c..cefab060b 100644 --- a/web/js/OpenLayers/theme/default/style.css +++ b/web/js/OpenLayers/theme/default/style.css @@ -3,11 +3,12 @@ div.olMap { padding: 0 !important; margin: 0 !important; cursor: default; + -ms-touch-action: none; + touch-action: none; } div.olMapViewport { text-align: left; - -ms-touch-action: none; } div.olLayerDiv { diff --git a/web/js/map-OpenLayers.js b/web/js/map-OpenLayers.js index 3de8e4d1f..49801911b 100644 --- a/web/js/map-OpenLayers.js +++ b/web/js/map-OpenLayers.js @@ -90,7 +90,8 @@ var fixmystreet = fixmystreet || {}; size: pin[5] || marker_size, faded: 0, id: pin[3], - title: pin[4] || '' + title: pin[4] || '', + draggable: pin[6] === false ? false : true }); markers.push( marker ); } @@ -144,7 +145,7 @@ var fixmystreet = fixmystreet || {}; admin_drag: function(pin_moved_callback, confirm_change) { confirm_change = confirm_change || false; var original_lonlat; - var drag = new OpenLayers.Control.DragFeature( fixmystreet.markers, { + var drag = new OpenLayers.Control.DragFeatureFMS( fixmystreet.markers, { onStart: function(feature, e) { // Keep track of where the feature started, so we can put it // back if the user cancels the operation. @@ -167,12 +168,41 @@ var fixmystreet = fixmystreet || {}; } ); fixmystreet.map.addControl( drag ); drag.activate(); + }, + + // `markers.redraw()` in markers_highlight will trigger an + // `overFeature` event if the mouse cursor is still over the same + // marker on the map, which would then run markers_highlight + // again, causing an infinite flicker while the cursor remains over + // the same marker. We really only want to redraw the markers when + // the cursor moves from one marker to another (ie: when there is an + // overFeature followed by an outFeature followed by an overFeature). + // Therefore, we keep track of the previous event in + // fixmystreet.latest_map_hover_event and only call markers_highlight + // if we know the previous event was different to the current one. + // (See the `overFeature` and `outFeature` callbacks inside of + // fixmystreet.select_feature). + + markers_highlight: function(problem_id) { + for (var i = 0; i < fixmystreet.markers.features.length; i++) { + if (typeof problem_id == 'undefined') { + // There is no highlighted marker, so unfade this marker + fixmystreet.markers.features[i].attributes.faded = 0; + } else if (problem_id == fixmystreet.markers.features[i].attributes.id) { + // This is the highlighted marker, unfade it + fixmystreet.markers.features[i].attributes.faded = 0; + } else { + // This is not the hightlighted marker, fade it + fixmystreet.markers.features[i].attributes.faded = 1; + } + } + fixmystreet.markers.redraw(); } }; var drag = { activate: function() { - this._drag = new OpenLayers.Control.DragFeature( fixmystreet.markers, { + this._drag = new OpenLayers.Control.DragFeatureFMS( fixmystreet.markers, { onComplete: function(feature, e) { fixmystreet.update_pin( feature.geometry ); } @@ -195,35 +225,6 @@ var fixmystreet = fixmystreet || {}; fixmystreet.map.setCenter(center, z); } - // `markers.redraw()` in markers_highlight will trigger an - // `overFeature` event if the mouse cursor is still over the same - // marker on the map, which would then run markers_highlight - // again, causing an infinite flicker while the cursor remains over - // the same marker. We really only want to redraw the markers when - // the cursor moves from one marker to another (ie: when there is an - // overFeature followed by an outFeature followed by an overFeature). - // Therefore, we keep track of the previous event in - // fixmystreet.latest_map_hover_event and only call markers_highlight - // if we know the previous event was different to the current one. - // (See the `overFeature` and `outFeature` callbacks inside of - // fixmystreet.select_feature). - - function markers_highlight(problem_id) { - for (var i = 0; i < fixmystreet.markers.features.length; i++) { - if (typeof problem_id == 'undefined') { - // There is no highlighted marker, so unfade this marker - fixmystreet.markers.features[i].attributes.faded = 0; - } else if (problem_id == fixmystreet.markers.features[i].attributes.id) { - // This is the highlighted marker, unfade it - fixmystreet.markers.features[i].attributes.faded = 0; - } else { - // This is not the hightlighted marker, fade it - fixmystreet.markers.features[i].attributes.faded = 1; - } - } - fixmystreet.markers.redraw(); - } - function sidebar_highlight(problem_id) { if (typeof problem_id !== 'undefined') { var $a = $('.item-list--reports a[href$="/' + problem_id + '"]'); @@ -505,7 +506,7 @@ var fixmystreet = fixmystreet || {}; overFeature: function (feature) { if (fixmystreet.latest_map_hover_event != 'overFeature') { document.getElementById('map').style.cursor = 'pointer'; - markers_highlight(feature.attributes.id); + fixmystreet.maps.markers_highlight(feature.attributes.id); sidebar_highlight(feature.attributes.id); fixmystreet.latest_map_hover_event = 'overFeature'; } @@ -513,7 +514,7 @@ var fixmystreet = fixmystreet || {}; outFeature: function (feature) { if (fixmystreet.latest_map_hover_event != 'outFeature') { document.getElementById('map').style.cursor = ''; - markers_highlight(); + fixmystreet.maps.markers_highlight(); sidebar_highlight(); fixmystreet.latest_map_hover_event = 'outFeature'; } @@ -606,6 +607,7 @@ var fixmystreet = fixmystreet || {}; // Create the basics of the map fixmystreet.map = new OpenLayers.Map( "map", OpenLayers.Util.extend({ + theme: null, controls: fixmystreet.controls, displayProjection: new OpenLayers.Projection("EPSG:4326") }, fixmystreet.map_options) @@ -665,9 +667,9 @@ var fixmystreet = fixmystreet || {}; var href = $('a', this).attr('href'); var id = parseInt(href.replace(/^.*[/]([0-9]+)$/, '$1')); clearTimeout(timeout); - markers_highlight(id); + fixmystreet.maps.markers_highlight(id); }).on('mouseleave', '.item-list--reports__item', function(){ - timeout = setTimeout(markers_highlight, 50); + timeout = setTimeout(fixmystreet.maps.markers_highlight, 50); }); })(); }); @@ -887,6 +889,19 @@ OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, { } }); +/* Drag handler that allows individual features to disable dragging */ +OpenLayers.Control.DragFeatureFMS = OpenLayers.Class(OpenLayers.Control.DragFeature, { + CLASS_NAME: "OpenLayers.Control.DragFeatureFMS", + + overFeature: function(feature) { + if (feature.attributes.draggable) { + return OpenLayers.Control.DragFeature.prototype.overFeature.call(this, feature); + } else { + return false; + } + } +}) + OpenLayers.Renderer.SVGBig = OpenLayers.Class(OpenLayers.Renderer.SVG, { MAX_PIXEL: 15E7, CLASS_NAME: "OpenLayers.Renderer.SVGBig" diff --git a/web/js/map-fms.js b/web/js/map-fms.js index 65c02a503..61206a90a 100644 --- a/web/js/map-fms.js +++ b/web/js/map-fms.js @@ -2,9 +2,6 @@ fixmystreet.maps.tile_base = [ [ '', 'a-', 'b-', 'c-' ], '//{S}tilma.mysociety.o fixmystreet.maps.config = (function(original) { return function(){ - if (fixmystreet.map_type) { - this.tile_base = fixmystreet.map_type; - } original(); fixmystreet.map_type = OpenLayers.Layer.BingUK; }; diff --git a/web/js/map-google.js b/web/js/map-google.js index 4c3f6188e..be2df8502 100644 --- a/web/js/map-google.js +++ b/web/js/map-google.js @@ -56,6 +56,9 @@ fixmystreet.maps = {}; google.maps.event.trigger(fixmystreet.map, 'idle'); }; + // This is a noop on Google Maps right now. + fixmystreet.maps.markers_highlight = function() {}; + function PaddingControl(div) { div.style.padding = '40px'; } |